Js function handwriting (1)

Js function handwriting (2)

1. Write a call

Function.prototype.myCall = function (context = window. args) {
	let fn = Symbol(a); context[fn] =this;
	letresult = context[fn](... args);delete context[fn];
	return result;
}
Copy the code

2. Apply handwriting

Function.prototype.myApply = function (context = window, args) {
    let fn = Symbol(a); context[fn] =this;
    letresult = context[fn](... args);delete context[fn];
    return result;
}
Copy the code

3. Write a bind

Function.prototype.myBind = function (context = window, ... args) { let fn = Symbol(); context[fn] = this; return function (... _args) { context[fn](... args, ... _args); delete context[fn]; }}Copy the code

4. Write a new

function myNew(fn, ... args) { var obj = Object.create(fn.prototype); var res = fn.apply(obj, args); return typeof res === 'object' ? res: obj; }Copy the code

5. Write a Object. The create

function create (proto) {
    function F() {}
    F.prototype = proto;
    return new F();
}
Copy the code

6. Handwriting ES5 inheritance

If you want student to inherit person, write down the basic framework

function person() {
	this.kind = "person";
}

person.prototype.eat = function (food) {
	console.log(this.name + " is eating " + food);
}

function student() {

}
Copy the code

Prototype inheritance

function person() {
	this.kind = "person";
}

person.prototype.eat = function (food) {
	console.log(this.name + " is eating " + food);
}

function student() {

}

student.prototype = new person();
Copy the code

Tectonic inheritance

function person() {
	this.kind = "person";
}

person.prototype.eat = function (food) {
	console.log(this.name + " is eating " + food);
}

function student( ) {
	person.call(this);
}
Copy the code

Combination of inheritance

function person() {
	this.kind = "person";
}

person.prototype.eat = function (food) {
	console.log(this.name + " is eating " + food);
}

function student() {
	person.call(this);
}

student.prototype = new person();
Copy the code

Primary inheritance

function person() {
	this.kind = "person";
}

person.prototype.eat = function (food) {
	console.log(this.name + " is eating " + food);
}

function student() {

}

student.prototype = Object.create(person.prototype);
Copy the code

Parasitic combinatorial inheritance

function person() { this.kind = "person"; } person.prototype.eat = function (food) { console.log(this.name + " is eating " + food); } function student() { person.call(this); } student.prototype = person.prototype; // Student. Prototype = object.create (person.prototype);Copy the code

Parasitic combinatorial optimization inheritance

function person() { this.kind = "person"; } person.prototype.eat = function (food) { console.log(this.name + " is eating " + food); } function student() { person.call(this); } student.prototype = person.prototype; // Student. Prototype = object.create (person.prototype); student.prototype.constructor = student;Copy the code

7. Manually implement Instanceof

function myInstanceof(target, origin) {
    let proto = target.__proto__;
    if (proto) {
        if (proto === origin.prototype) {
            return true;
        } else {
            returnmyInstanceof(proto, origin); }}else {
        return false; }}Copy the code

8. Handwritten Array. IsArray

Implement array.isarray using toString

Array.myIsArray = function(obj) {
	return Object.prototype.toString.call(Object(obj)) === '[object Array]';
}
console.log(Array.myIsArray([])); // true
Copy the code

Implement array.isarray with instanceof

Array.myIsArray = function(obj) {
	return obj instanceof Array;
}
console.log(Array.myIsArray([])); // true
Copy the code

Implement array.isarray using constructor

Array.myIsArray = function(obj) {
	return obj.constructor === Array;
}
console.log(Array.myIsArray([])); // true
Copy the code

9. Implement a function to determine the data type

function getType(obj) {
    if (obj === null) return String(obj);
    // Object type "[object XXX]"->XXX of the lower case simple typeof obj
    return typeof obj === 'object' ? Object.prototype.toString.call(obj).replace('[object '.' ').replace('] '.' ').toLowerCase() : typeof obj;
}

/ / call
console.log(getType(null)); // -> null
console.log(getType(undefined)); // -> undefined
console.log(getType({})); // -> object
console.log(getType([])); // -> array
console.log(getType(123)); // -> number
console.log(getType(true)); // -> boolean
console.log(getType('123')); // -> string
console.log(getType(/ 123 /)); // -> regexp
console.log(getType(new Date())); // -> date
Copy the code

10. Deep copy by hand

The minimalist version

JSON.parse(JSON.stringify(obj))
Copy the code

Limitations of this method:

  • Special objects such as functions and regexps cannot be cloned
  • Object constructor is discarded and all constructors point to Object
  • Object has a circular reference and an error is reported
  • All properties with symbol as the property key are completely ignored
  • Unable to partition Values, numbers, strings and their wrapping objects
  • Values and nulls in NaN and Infinity formats are treated as null.
  • Other types of objects, including Map/Set/WeakMap/WeakSet, serialize only enumerable properties.

The recursive method

Consider arrays and objects

function deepCopy(obj) { let res; Type === "object" if (typeof obj === "object" &&obj! == null) {// The type of complex data types res = obj.constructor === Array? [] : {}; For (let I in obj) {res[I] = typeof obj[I] === "object"? deepCopy(obj[i]) : obj[i]; }} else {// Simple data type direct = assignment res = obj; } return res; }Copy the code

A circular reference

The above version executes the following test case:

function deepCopy(obj) {
	let res;
	Typeof null === "object"
	if (typeof obj === "object"&& obj ! = =null) {
		// The type of the complex data type
		res = obj.constructor === Array ? [] : {};
		for (let i in obj) {
			// Iterate over whether each element in the object is an object type
			res[i] = typeof obj[i] === "object"? deepCopy(obj[i]) : obj[i]; }}else {
		// Simple data types directly = assignment
		res = obj;
	}
	return res;
}

let a = {
    val: 2
};
a.target = a;
let res = deepCopy(a);
console.log(res.target);
/* VM948:12 Uncaught RangeError: Maximum call stack size exceeded
	....
*/
Copy the code

The stack ran out of memory because the recursion went into an infinite loop.

The reason is that the above object has a circular reference, that is, the object’s attributes refer to itself indirectly or directly:

Solve the problem of circular reference, we can create a extra storage space, to store the current objects and copy the object correspondence, when need to copy the current object, go to the storage space, find ever copy this object, if any direct return, if not to copy, so clever resolve problem of circular references.

This storage space needs to be able to store key-value data, and key can be a reference type, we can choose Map data structure:

  • checkmapIs there any cloned object in
  • Yes. – Straight back
  • None – takes the current object askey, clone the object asvalueFor storage
  • Continue to clone
function deepCopy(obj, map = new Map(a)) {
	let res;
	Typeof null === "object"
	if (typeof obj === "object"&& obj ! = =null) {
		// The type of the complex data type
		res = obj.constructor === Array ? [] : {};
        // If there are cloned objects in map, return directly
        if (map.get(obj)) {
            return obj;
        }
        // No cloned objects in map are stored
        map.set(obj, res);
		for (let i in obj) {
			// Iterate over whether each element in the object is an object type
			res[i] = typeof obj[i] === "object"? deepCopy(obj[i], map) : obj[i]; }}else {
		// Simple data types directly = assignment
		res = obj;
	}
	return res;
}

/ / test
let a = {
    val: 2
};
a.target = a;
let res = deepCopy(a);
console.log(res.target);
Copy the code

11. Array flattening

Multidimensional arrays => one-dimensional arrays

let arr = [1[2[3[4.5]]].6];// -> [1, 2, 3, 4, 5, 6]
Copy the code

Call the flat method in ES6

function flat(arr) {
    return arr.flat(Infinity);
}
let arr = [1, [2, [3, [4, ,5]]], 6];
console.log(flat(arr));
// [1, 2, 3, 4, 5, 6]
Copy the code

Regular expression

function flat(arr) {
    return JSON.stringify(arr).replace(/\[|\]/g, '').split(',');
}
let arr = [1, [2, [3, [4, ,5]]], 6];
console.log(flat(arr));
Copy the code

Parse to the original format using json.parse

function flat(arr) {
    let str = '[' + JSON.stringify(arr).replace(/\[|\]/g, '').split(',') + ']';
    return JSON.parse(str);
}
let arr = [1, [2, [3, [4, ,5]]], 6];
console.log(flat(arr));
// [1, 2, 3, 4, null, 5, 6]
Copy the code

recursive

function flat(arr) { let res = []; for (let item of arr) { if (item.constructor === Array) { res.push(... flat(item)); } else { res.push(item); } } return res; } let arr = [1, [2, [3, [4, ,5]]], 6]; console.log(flat(arr));Copy the code

ES5’s treatment of empty seats is very inconsistent, ignoring empty seats in most cases.

  • forEach().filter().reduce().every()some()All jump over the empty space.
  • map()The void is skipped, but the value is preserved.
  • join()toString()Will treat the empty space asundefinedAnd theundefinednullWill be processed as an empty string.

ES6 explicitly converts the void toundefined.

  • entries(),keys(),values(),find()findIndex()Will process the empty space intoundefined.
  • for... ofThe loop traverses the empty space.
  • fill()Empty Spaces are treated as normal array positions.
  • copyWithin()Will copy with the empty space.
  • Extended operator (.) will also convert the empty space toundefined.
  • Array.fromMethod will convert the empty space of the array toundefined.

Array traversal using forEach can be broken

function flat(arr) { let res = []; arr.forEach(function (item){ if (item.constructor == Array) { // if (instanceof(item) == Array) { res.push(... flat(item)); } else { res.push(item); } }) return res; } let arr = [1, [2, [3, [4, ,5]]], 6]; console.log(flat(arr)); // [1, 2, 3, 4, 5, 6]Copy the code

Iterating with reduce functions (in place)

Implement using Reduce

function flat(arr) { return arr.reduce((pre, cur) => { return pre.concat(Array.isArray(cur) ? flat(cur) : cur); } []); } let arr = [1, [2, [3, [4, ,5]]], 6]; console.log(flat(arr)); // [1, 2, 3, 4, 5, 6]Copy the code

Extended operator

// As long as one element has an array, the loop continues
let arr = [1[2[3[4.5]]].6];
while (arr.some(Array.isArray)) { arr = [].concat(... arr); }console.log(arr);
// [1, 2, 3, 4, empty, 5, 6]
Copy the code

12. Array deduplication

Double-layer for loop

function unique(arr) {
    for (let i = 0, len = arr.length; i < len; i++) {
        for (let j = i + 1; j < len; j++) {
            // Prevent type conversion if ==NaN only one, null disappears 3
            if (arr[i] === arr[j]) {
                // Splice changes the array length, so subtract len and j by one
                arr.splice(j, 1); len--; j--; }}}return arr;
}

let arr = [1.1.'true'.'true'.true.true.15.15.false.false.undefined.undefined.null.null.NaN.NaN.'NaN'.0.0.'a'.'a', {}, {}];
console.log(unique(arr));
/ / (1, "true", true, 15, false, undefined, null, NaN, NaN, "NaN," 0, "a", {...}, {...}] / / NaN, {} not to heavy
Copy the code

Use indexOf or includes to remove weights

Create an empty result array, loop through the original array, and check whether the result array has the current element. If they have the same value, skip it. If they do not, push into the array Object

Use indexOf to determine

function unique(arr) {
    let res = [];
    for (let i = 0; i < arr.length; i++) {
        if (res.indexOf(arr[i]) === -1) { res.push(arr[i]); }}return res;
}

let arr = [1.1.'true'.'true'.true.true.15.15.false.false.undefined.undefined.null.null.NaN.NaN.'NaN'.0.0.'a'.'a', {}, {}];
console.log(unique(arr));
/ / (1, "true", true, 15, false, undefined, null, NaN, NaN, "NaN," 0, "a", {...}, {...}] / / NaN, {} not to heavy
Copy the code

Use includes to determine

function unique(arr) { let res = []; for (let i = 0; i < arr.length; i++) { if (! res.includes(arr[i])) { res.push(arr[i]); } } return res; } let arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}]; console.log(unique(arr)); / / (1, "true", true, 15, false, undefined, null, NaN, "NaN," 0, "a", {...}, {...}] / / {} not to heavyCopy the code

Use ES6 Set to de-weigh

function unique (arr) {
    return Array.from(new Set(arr));
}

let arr = [1.1.'true'.'true'.true.true.15.15.false.false.undefined.undefined.null.null.NaN.NaN.'NaN'.0.0.'a'.'a', {}, {}];
console.log(unique(arr));
/ / (1, "true", true, 15, false, undefined, null, NaN, "NaN," 0, "a", {...}, {...}] / / {} not to heavy
Copy the code

Using the expansion operator shorthand

function unique (arr) { return [...new Set(arr)]; } let arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}]; console.log(unique(arr)); / / (1, "true", true, 15, false, undefined, null, NaN, "NaN," 0, "a", {...}, {...}] / / {} not to heavyCopy the code

Using hasOwnProperty

Using hasOwnProperty to determine the existence of Object properties this method takes an empty Object and stores the value of the array as the key of the Object. 1 is not the same as ‘1’, but it is the same as the key. This is because the key can only be a string, so we can use typeof item + item as the key to avoid this problem:

Implement using filter

function unique(arr) { var obj = {}; return arr.filter(function(item){ return obj,hasOwnProperty(typeof item + item) ? false : (obj[typeof iten + item] = true); }) } let arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}]; console.log(unique(arr)); / / (1, "true", true, 15, false, undefined, null, NaN, "NaN," 0, "a", {...}] / / NaN and {} to heavyCopy the code

13. Handwriting array ES5 common methods

Parameters that

  • Callback callback function
  • This value used by context to perform callback
  • The element being processed in the current array
  • Index Current index
  • Array source array
  • An accumulator accumulator
  • InitialValue Reduce or reduceRight Default value of the first parameter when callbackFn is called for the first time
  • Self implements this object

1. The forEach function

Arr. ForEach (callback(current [, index [, array]])[, context])

The ** callback argument is: each item, index, primitive array, each element of the array to execute the given function once.

Return: undefined.

Custom function: myForEach.

Array.prototype.myForEach = function(callback, context) { if (typeof callback ! == 'function') throw ('callback must be a function'); let self = this, len = self && element.length || 0; if (! context) context = self; for (let index = 0; index < len; index++) { callback.call(context, self[index], index, self); }};Copy the code

2. The filter function

Var newArray = arr.filter(callback(current[, index[, array]])[, context])

Method function: Creates a new array that filters out items not returned by the callback function that contain all elements of the test implemented by the provided function.

Returns a new array of elements that have passed the test, or an empty array if none of the array elements have passed the test.

Custom function: myFilter.

Array.prototype.myFilter = function(callback, context) { if (typeof callback ! == 'function') throw ('callback must be a function'); let self = this, len = self && self.length || 0, newArray = []; if (! context) context = self; for (let index = 0; index < len; index++) { if (callback.call(context, self[index], index, self)) newArray.push(self[index]); } return newArray; };Copy the code

3. The find function

Arr. Find (callback[, context])

Method function: Returns the value of the first element in the array that satisfies the provided test function. Otherwise return undefined.

Returns the value of the first element in the array that satisfies the provided test function, otherwise undefined.

Custom function: myFind.

Array.prototype.myFind = function(callback, context) { if (typeof callback ! == 'function') throw ('callback must be a function'); let self = this, len = self && self.length || 0; if (! context) context = self; for (let index = 0; index < len; index++) { if (callback.call(context, self[index], index, self)) { return self[index]; } } return undefined; }Copy the code

4. FindIndex function

Arr. FindIndex (callback[, context])

Function: returns the index of the first element in the array that satisfies the provided test function. Otherwise -1 is returned.

Returns: the index of the first element in the array by providing the test function. Otherwise, -1 is returned.

Custom function: myFindIndex.

Array.prototype.myFindIndex = function(callback, context) { if (typeof callback ! == 'function') throw ('callback must be a function'); let self = this, len = self && self.length || 0; if (! context) context = self; for (let index = 0; index < len; index++) { if (callback.call(context, self[index], index, self)) return index; } return -1; }Copy the code

5. Fill functions

Arr. Fill (value[, start[, end]])

The start index () function populates an array with a fixed value from the start index to the end index. Does not include terminating indexes.

Return: Returns the value replaced by the original array changed.

Custom function: myFill.

Array.prototype.myFill = function(value, start = 0, end) { let self = this, len = self && self.length || 0; end = end || len; let loopStart = start < 0 ? 0: start, // set the value loopEnd = end >= len? len : end; // Set the loop end value for (; loopStart < loopEnd; loopStart++) { self[loopStart] = value; } return self; }Copy the code

6. The map function

Var newArray = arr.map(function callback(current[, index[, array]]) {// Return self for newArray}[, context])

The createarray () method creates a new array with the result that each element in the array is the value returned by calling the supplied function once.

Returns: tests whether at least 1 element in the array passes the provided function test. It returns a Boolean value. A new array consisting of the result of each element of the original array executing the callback function.

Custom function: myMap.

Array.prototype.myMap = function(callback, context) { if (typeof callback ! == 'function') throw ('callback must be a function'); let self = this, len = self && self.length || 0, result = []; if (! context) context = self; for (let index = 0; index < len; index++) { result[index] = callback.call(context, self[index], index, self); } return result; }Copy the code

7. Some function

Arr. Some (callback(current[, index[, array]])[, context])

Function: tests if at least one element in the array passes the provided function test. The result is true if one element passes the provided function test, otherwise false. It returns a Boolean value.

Returns true if at least one element in the array passes the callback test; The return value is false only if all elements fail the callback test.

Custom function: mySome.

Array.prototype.mySome = function(callback, context) { if (typeof callback ! == 'function') throw ('callback must be a function'); let self = this, len = self && self.length || 0; if (! context) context = self; for (let index = 0; index < len; index++) { if (callback.call(context, self[index], index, self)) return true; } return false; }Copy the code

8. Every function

Arr. Every (callback(current[, index[, array]])[, context])

Function: tests whether all elements in an array pass the test of a specified function. The result is true if all callbacks return true, and false otherwise. It returns a Boolean value.

Return: True if the callback function returns true each time, false otherwise.

Custom function: myEvery.

Array.prototype.myEvery = function(callback, context) { if (typeof callback ! == 'function') throw ('callback must be a function'); let self = this, len = self && self.length || 0; if (! context) context = self; for(let index = 0; index < len; index++) { if (! callback.call(context, element[index], index, element)) return false; } return true; }Copy the code

9. The reduce function

Arr. reduce(callback(accumulator, current[, index[, array]])[, initialValue])

Function of the ** method: ** performs a Reducer function (in ascending order) provided by you on each element in the array and summarizes its results into a single return value. The last callback function returns the result of reduce. You can specify the cumulative initial value. If you do not specify the initial value, the second item will be iterated

Returns the result of the cumulative processing of the function.

Custom function: myReduce.

Array.prototype.myReduce = function(callback, initialValue) { if (typeof callback ! == 'function') throw ('callback must be a function'); let self = this, len = self.length || 0; let result = initialValue ? initialValue : self[0]; // Let index = initialValue? 0:1; while (index < len) { if (index in self) result = callback(result, self[index], index, self); index++; } return result; }Copy the code

14. Implement array inversion in place

We use a double pointer, a third variable swap

function revert(arr, start, end) {
	while (start < end) {
		let temp = arr[start];
		arr[start] = arr[end];
		arr[end] = temp;
        start++;
        end--;
	}
}

let arr = [0, 1, 4, 9, 16, 25];
revert(arr, 2, 5);
console.log(arr);
// [0, 1, 25, 16, 9, 4]
Copy the code

Deconstruct the assignment,

function revert(arr, start, end) {
	while (start < end) {
		[arr[start], arr[end]] = [arr[end], arr[start]];
        start++;
        end--;
	}
}

let arr = [0, 1, 4, 9, 16, 25];
revert(arr, 2, 5);
console.log(arr);
// [0, 1, 25, 16, 9, 4]
Copy the code

Using sums or bits

function revert(arr, start, end) {
	while (start < end) {
		arr[start] += arr[end];
        arr[end] = arr[start] - arr[end];
        arr[start] = arr[start] - arr[end];
        start++;
        end--;
	}
}

let arr = [0, 1, 4, 9, 16, 25];
revert(arr, 2, 5);
console.log(arr);
// [0, 1, 25, 16, 9, 4]
Copy the code

or

function revert(arr, start, end) {
	while (start < end) {
		arr[start] ^= arr[end];
        arr[end] ^= arr[start];
        arr[start] ^= arr[end];
        start++;
        end--;
	}
}

let arr = [0, 1, 4, 9, 16, 25];
revert(arr, 2, 5);
console.log(arr);
Copy the code

15. Reduce application summary

The reduce of grammar

array.reduce(function(total, currentValue, currentIndex, arr), initialValue);
/* total: required. The initial value, or the return value at the end of the calculation. CurrentValue: Required. Current element. CurrentIndex: Optional. Index of the current element; Arr: Optional. The array object to which the current element belongs. The initialValue: optional. The initial value passed to the function is equivalent to the initial value of total. * /
Copy the code

ReduceRight (), the usage of this method is actually the same as reduce(), but the traversal order is opposite. It starts from the last item of the array and traverses forward to the first item

Array sum

Basic version

const arr = [12, 34, 23]; const sum = arr.reduce((total, num) => total + num); console.log(sum); / / 69Copy the code

Set the initial value to sum

const arr = [12, 34, 23]; const sum = arr.reduce((total, num) => total + num, 10); // Sum console.log(sum) with the initial value 10; / / 79Copy the code

Array maximum

const arr = [23.123.342.12];
const max = arr.reduce((pre, cur) = > pre > cur ? pre : cur, Number.MIN_SAFE_INTEGER);
console.log(max); / / 342
Copy the code

Array to object

var streams = [{name: 'technology'.id: 1}, {name: 'design'.id: 2}];
var obj = streams.reduce((accumulator, cur) = > {accumulator[cur.id] = cur; returnaccumulator; }, {});Copy the code

Array flattening

See Array flattening

Array to heavy

See [Array deduplication](# array.reduce () + indexOf/includes)

Find the number of occurrences of letters in a string

const str = 'sfhjasfjgfasjuwqrqadqeiqsajsdaiwqdaklldflas-cmxzmnha';

const res = str.split(' ').reduce((count, next) = > {
    count[next] ? count[next]++ : count[next] = 1;
    returncount; }, {});console.log(res);
/ / the result
/* -: 1 a: 8 c: 1 d: 4 e: 1 f: 4 g: 1 h: 2 i: 2 j: 4 k: 1 l: 3 m: 2 n: 1 q: 5 r: 1 s: 6 u: 1 w: 2 x: 1 z: 1 */
Copy the code

Compose function

Redux compose source code implementation

function compose(. funs) {
    if (funs.length === 0) {
        return arg= > arg;
    }
    if (funs.length === 1) {
       return funs[0];
    }
    return funs.reduce((a, b) = > (. arg) = >a(b(... arg))) }const partial = (fn, ... args) = > (. _args) = >fn(... args, ... _args);const partialRight = (fn, ... args) = > (. _args) = >fn(... _args, ... args);function add(x, y) {
	return x + y;
}

function pow(x, y) {
  	return Math.pow(x, y);
}

function double(x) {
  	return x * 2;
}

function multiply(x, y) {
	return x * y;
}

compose(
    console.log, 
    partial(add, 10),
    partialRight(pow, 3),
    partial(multiply, 5(a))2); / / 1010
Copy the code

Or use reduceRight

function compose(... funs) { if (funs.length === 0) { return arg => arg; } if (funs.length === 1) { return funs[0]; } return funs.reduceRight((a, b) => (... arg) => b(a(... arg))) } const partial = (fn, ... args) => (... _args) => fn(... args, ... _args); const partialRight = (fn, ... args) => (... _args) => fn(... _args, ... args); function add(x, y) { return x + y; } function pow(x, y) { return Math.pow(x, y); } function double(x) { return x * 2; } function multiply(x, y) { return x * y; } compose( console.log, partial(add, 10), partialRight(pow, 3), partial(multiply, 5) )(2); / / 1010Copy the code

For more information, see the pencompose function

Implement multi-dimensional array traceback

[[‘ a ‘, ‘b’], [‘ n ‘, ‘m’], [‘ 0 ‘, ‘1’]] = > [” an0 “, “an1”, “am0”, “am1,” “bn0”, “bn1”, “bm0”, “bm1”]

function backtrack(arr) {
	return arr.reduce((prev, cur) => {
		let list = [];
		for (let i = 0; i < prev.length; i++) {
			for (let j = 0; j < cur.length; j++) {
				list.push(prev[i] + cur[j]);
			}
		}
        return list;
	})
}

console.log(backtrack([['a', 'b'], ['n', 'm'], ['0', '1']]));
// ["an0", "an1", "am0", "am1", "bn0", "bn1", "bm0", "bm1"]
Copy the code

16. Shuffle algorithm

In the simplest form, you swap as you traverse

function shuffle(array) {
    const length = array.length;
    for (let i = 0; i < length; i++) {
        let random = Math.floor(length * Math.random()); [array[i], array[random]] = [array[random], array[i]]; }}let arr = Array.from(Array(100), (item, index) = > index);
shuffle(arr);
console.log(arr);
Copy the code

Recognized mature fisher-Yates shuffle algorithm, the simple idea is as follows:

    1. Defines an array with the last element of the array as its reference point.
    1. Take a random position between the beginning of the array and the reference point, and swap the elements at the selected position with the elements at the reference point.
    1. Base point shifted one to the left.
    1. Repeat steps 2,3 until the reference point is the beginning of the array.
function shuffle(arr) { let length = arr.length; for (let i = length - 1; i >= 0; i--) { let random = Math.floor(Math.random() * (i + 1)); // Generate a random position between the starting position and the base position, and move the base continuously left from the end position. Var newA = arr[I]; // arr[i] = arr[random]; // arr[random] = newA; / / es6 [arr [I], arr [random]] = [arr (random), arr [I]]. // Swap element positions. } return arr; } let arr = Array.from(Array(100), (item, index) => index); shuffle(arr); console.log(arr);Copy the code

17. Object flattening

Implement an objectFlat function to implement the following conversion functions

const obj = { a: 1, b: [1, 2, { c: true }], c: { e: 2, f: 3 }, g: null, }; / / convert the let objRes = {a: 1, "b [0]" : 1, "b [1]" : 2, "b [2]. C:" true, "c.e" : 2, "c.f" : 3, g: null,};Copy the code

From the result, we can know that we need to traverse the object and output the property values in turn, so we can know that the core method body is: pass in the key value and value of the object, and recursively traverse the value.

We know that JS data types can be base data types and reference data types. As for the topic, there is no need for in-depth traversal of basic data types, while reference data types need to be recursed again.

function objectFlat(obj = {}) {
    const res = {};
    function flat(value, key = ' ') {
        // Determine whether it is an underlying data type or a reference data type
        if (typeofvalue ! = ="object" || value === null) {
            // Base data type
            if(key) { res[key] = value; }}else if (Array.isArray(value)) { // It is an array
            for (let i = 0; i < value.length; i++) {
                flat(value[i], key + ` [${i}] `); }}else { // Judgments are objects
            let keys = Object.keys(value);
            keys.forEach(item= > {
                flat(value[item], key ? `${key}.${item}` : `${item}`);
            })
            / / null objects
            if(! keys.length && key) { res[key] = {}; } } } flat(obj);return res;
}

/ / test
const source = {
    a: {
        b: [
            1.2,
            {
                c: 1.d: 2}].e: 3
    },
    f: {
        g: 2}};console.log(objectFlat(source));
/* a.b[0]: 1 a.b[1]: 2 a.b[2].c: 1 a.b[2].d: 2 a.e: 3 f.g: 2 */
Copy the code

18. Handwriting partial functions

One day in an interview, the interviewer gave me a handwritten code problem

/** * The partialUsingArguments function is partialUsingArguments. Fn (*/); fn (partialUsingArguments */); fn (partialUsingArguments */)Copy the code

I was the first edition of thinking, the joining together of two parameters array, back through the closure as a result, the interviewer prompt if the parameter is empty, what should I do, I added the args = args | | []; This sentence

function partialUsingArguments(fn, ... args) { args = args || []; return function (... _args) { return fn(args.concat(_args)); }}Copy the code

What if the argument is not an array, but an object

Regular writing

function partialUsingArguments(fn, args) { return function (_args) { return fn(... args, ... _args); }}Copy the code

Simplify the writing

const partialUsingArguments = (fn, ... args) => (... _args) => fn(... args, ... _args);Copy the code

19. Corrification of functions

define

The technique of converting a function that takes multiple arguments into a function that takes a single argument (the first argument of the original function) and returns a new function that takes the remaining arguments and returns the result

Closures are used to store arguments, and when there are enough arguments to execute the function, the function is executed.

Regular writing

function curry(fn, ... Args) {if (args. Length >= fn.length) {// Check whether the parameter passed by the current function is greater than or equal to the number of parameters required by fn. args); } else {// If the number of parameters passed in is not enough, return a closure that temporarily stores the parameters passed in, and return the curry function again (... _args) => curry(fn, ... args, ... _args); } } function multiFn(a, b, c) { return a * b * c; } var multi = curry(multiFn); console.log(multi(2)(3)(4)); // 24 console.log(multi(3, 4, 5)); // 60 console.log(multi(4)(5, 6)); // 120 console.log(multi(5, 6)(7)); / / 210Copy the code

Simplify the writing

const curry = (fn, arr = []) => (... _args) => ( args => args.length === fn.length ? fn(... args) : curry(fn, args) )([...arr, ..._args]) function multiFn(a, b, c) { return a * b * c; } var multi = curry(multiFn); console.log(multi(2)(3)(4)); // 24 console.log(multi(3, 4, 5)); // 60 console.log(multi(4)(5, 6)); // 120 console.log(multi(5, 6)(7)); / / 210Copy the code

20. Handwriting compose function

Stack-based compose function

function compose(... args) { return function(result) { const funcs = [...args]; while(funcs.length > 0) { result = funcs.pop()(result); } return result; }; } // import { partial, partialRight } from 'lodash'; const partial = (fn, ... args) => (... _args) => fn(... args, ... _args); const partialRight = (fn, ... args) => (... _args) => fn(... _args, ... args); function add(x, y) { return x + y; } function pow(x, y) { return Math.pow(x, y); } function double(x) { return x * 2; } const add10 = partial(add, 10); const pow3 = partialRight(pow, 3); compose(console.log, add10, pow3, double)(2) // 74Copy the code

Use the compose function of the reduce function

/* function compose(... funcs) { return funcs .reverse() .reduce((fn1, fn2) => (... args) => fn2(fn1(... args))); } */ function compose(... funcs) { return funcs .reduce((fn1, fn2) => (... args) => fn1(fn2(... args))); } // import { partial, partialRight } from 'lodash'; const partial = (fn, ... args) => (... _args) => fn(... args, ... _args); const partialRight = (fn, ... args) => (... _args) => fn(... _args, ... args); function add(x, y) { return x + y; } function pow(x, y) { return Math.pow(x, y); } function double(x) { return x * 2; } const add10 = partial(add, 10); const pow3 = partialRight(pow, 3); compose(console.log, add10, pow3, double)(2) // 74Copy the code

21. Implement (5).add(3).minus(2)

Example: 5 + 3-2, the result is 6

Number.prototype.add = function(n) {
    return this.valueOf() + n;
}
Number.prototype.minus = function(n) {
    return this.valueOf() + n;
}
console.log((5).add(3).minus(2)); / / 6
Copy the code

22. Implement an Add function

Meet the following functions

add(1); / / 1
add(1) (2); / / 3
add(1) (2) (3); / / 6
add(1) (2.3); / / 6
add(1.2) (3); / / 6
add(1.2.3); / / 6
Copy the code

You need to combine the above partial function with the toString() method, which is automatically called when the function is printed

function add(. args) {
    let fn = function(. _args) {
        returnadd(... args, ... _args); } fn.toString =function() {
        return args.reduce((a, b) = > a + b);
    }
    
    return fn;
}
console.log(add(1)); / / 1
console.log(add(1) (2)); / / 3
console.log(add(1) (2) (3)); / / 6
console.log(add(1) (2.3)); / / 6
console.log(add(1.2) (3)); / / 6
console.log(add(1.2.3)); / / 6
Copy the code

Compute the intersection of two arrays

For example, given nums1 = [1, 2, 2, 1], nums2 = [2, 2], return [2, 2].

Sort + double pointer

function union (nums1, nums2) {
    nums1.sort((x, y) = > x - y);
    nums2.sort((x, y) = > x - y);
    const length1 = nums1.length, length2 = nums2.length;
    / / double pointer
    let index1 = 0, index2 = 0;
    const intersection = [];
    while (index1 < length1 && index2 < length2) {
        const num1 = nums1[index1], num2 = nums2[index2];
        if (num1 === num2) {
            intersection.push(num1);
            index1++;
            index2++;
        } else if (num1 < num2) {
            index1++;
        } else{ index2++; }}return intersection;
};

 const a = [1.2.2.1];
 const b = [2.3.2];
 console.log(union(a, b)); / / (2, 2)
Copy the code

24. Depth comparison of handwritten objects

To compare two objects in depth, you need to compare each element in depth. = > recursion

  • Recursive exit conditions:
    • Two value type variables are compared and are judged directly by “===”
    • One of the two variables being compared is NULL, which directly determines whether the other element is also null
  • End recursion early:
    • The two variables have different numbers of keys
    • The two arguments passed in are the same variable
  • Recursion works: – Compare each key in depth
Function isEqual(obj1, obj2){// Null if (! isObject(obj1) || ! isObject(obj2)) return obj1 === obj2; If (obj1 === obj2) return true; Const obj1Keys = object.keys (obj1); const obj2Keys = Object.keys(obj2); if(obj1Keys.length ! == obj2Keys.length) return false; // Compare each key for(let key of obj1Keys){// Recursive query if (! isEqual(obj1[key], obj2[key])) return false; } return true; }Copy the code

25. Flat array to tree structure

How to convert data to result (handwritten code)

Const data = [{id: 10, parentId: 0, text: "parentId: 1"}, {id: 20, parentId: 0, text:" parentId: 2"}, {id: 30, parentId: 1 20, text: "parentId: 3"}, {id: 25, parentId: 30, text:" parentId: 25"}, {id: 35, parentId: 30, text: "parentId: 30"}]; Let result = [{id: 10, text: 'parentId -1', parentId: 0}, {id: 20, text:' parentId -2', parentId: 0, children: [{id: 10, text: 'parentId -2', parentId: 0, children: [ 10, the text: 'menu - 3' parentId: 20, children: [...]}}]];Copy the code

At first, I thought that there was only one layer of child nodes, so I planned to put those with child nodes in first, and then iterate over those with parents. After half of writing, I rearranged my ideas, and wrote the following code, sorting by ID from smallest to largest, traversing backwards, and inserting the child nodes into the children array of the parent node

Const data = [{id: 10, parentId: 0, text: "parentId: 1"}, {id: 20, parentId: 0, text:" parentId: 2"}, {id: 30, parentId: 1 20, text: "parentId: 3"}, {id: 25, parentId: 30, text:" parentId: 25"}, {id: 35, parentId: 30, text: "parentId: 30"}]; function convert(data) { data.sort((a, b) => a.parentId - b.parentId); for (let i = data.length - 1; i>= 0; i--) { if (data[i].parentId === 0) break; for (let j = i - 1; j >= 0; j--) { if (findParent(i, j)) { break; } } } return data; function findParent(a, b) { if (data[a].parentId == data[b].id) { data[b].children = data[b].children || []; data[b].children.push(data.splice(a, 1)); return true; } return false; } } console.log(convert(data));Copy the code

For multiple conversion methods, refer to the JS tree structure

26. The image stabilization (debounce)

No matter how frequently the event is triggered, it must be executed n seconds after the event is triggered. If you trigger the event within n seconds of the event being triggered, the event will be executed n seconds after the time of the new event. In short, the event will not be triggered for n seconds after the event is triggered, and the event will be executed n seconds later.

Applicable scenarios:

Button submission scenario: prevent multiple submission of the button and only perform the last submission of a server verification scenario: form verification requires the cooperation of the server, only perform a sequence of input events for the last time, and search for associative words similar functions

Anti-shake Code (first edition)

From this statement, we can write the first version of the code:

Function debounce(func, wait) {var timeout; return function () { clearTimeout(timeout) timeout = setTimeout(func, wait); }}Copy the code

This Point (2nd edition)

But using the first debounce function, this would point to the Window object, not the corresponding element! So we need to point this to the right object.

Let’s modify the code:

Function debounce(func, wait) {function debounce(func, wait) {let timer = 0; Return function () {let context = this; return function () {let context = this; if (timer) clearTimeout(timer); timer = setTimeout(function(){ func.apply(context); }, wait); Function debounce(func, wait = 50) {function debounce(func, wait = 50) { // If the timer is set, the last timer is cleared. // Start a new timer. Return function() {if (timer) clearTimeout(timer); timer = setTimeout(() => { func.apply(this); }, wait) } }Copy the code

Now this is pointing correctly

Correct parameter transfer (3rd version, basic functions have been completed)

The above code does not protect functions with arguments and needs to be improved

So let’s change the code again:

Function debounce(func, wait = 50) {function debounce(func, wait = 50) { // Empty the last timer if it has already been set // Start a new timer, delay the execution of the method passed by the user return function(... args) { if (timer) clearTimeout(timer); timer = setTimeout(() => { func.apply(this, args); }, wait) } }Copy the code

For a richer taxonomy of underscore functions, see the JavaScript topic Below

27. The throttle (throttle)

What is throttling

Throttling specifies that functions can only be triggered once per unit of time. If more than one function is fired in this unit of time, only one function will take effect

Stabilization is delayed, and the throttle is executive, interval function throttling namely every once in a while is executed once, implementation principle to set a timer, convention xx milliseconds after the event, if the time is up, so the executive function and reset the timer, and if the difference is that if each trigger reset timer, Throttling empties the timer after the timer reaches the time limit. Specifies that a function can fire only once per unit of time. If more than one function is fired in this unit of time, only one function will take effect.

There are two main ways to implement throttling, one is to use time stamps, the other is to set timers.

Applicable scenarios:

  • Drag scenario: Perform this operation only once within a fixed period to prevent uHF position changes
  • Zoom scene: Monitor the browserresize
  • Animated scenes: Avoid performance issues caused by multiple animations in a short period of time

Timestamp version code

Let’s take a look at the first method: use the timestamp, when the triggering event, we remove the current timestamp, then minus the timestamp before (the most value to 0) at the beginning, if is greater than the set period of time, executive function, and then update timestamp for the current timestamp, if less than, can not perform.

Function throttle (func, wait = 50) {function throttle (func, wait = 50) {let lastTime = 0; return function (... Args) {// Let now = +new Date(); If (now-lasttime > wait) {lastTime = now; if (now-lasttime > wait) {lastTime = now; func.apply(this, args); Timer setInterval(throttle(() => {console.log(1)}, 500), 1);Copy the code

Timer code

When the event is triggered, we set a timer, and when the event is triggered, if the timer exists, it is not executed until the timer is executed, and then the function is executed to clear the timer so that the next timer can be set.

Function throttle(func, wait = 50) {function throttle(func, wait = 50) { return function(... args) { let context = this; if (! timer) { timer = setTimeout(function() { timer = null; func.apply(context, args); // throttle(func, wait = 50) {// Throttle (func, wait = 50) {// Throttle (func, wait = 50) {// throttle(func, wait = 50) {// throttle(func, wait = 50); return function(... args) { if (! timer) { timer = setTimeout(() => { timer = null; func.apply(context, args); Timer setInterval(throttle(() => {console.log(1)}, 500), 1)Copy the code

Prev old timestamp and now new timestamp are used. Each event is triggered to determine the time difference between the two methods. If the specified time is reached, the function is executed and the old timestamp is reset to compare the two methods:

  1. The first event is executed immediately, and the second event is executed for the first time in n seconds
  2. After the first event stops firing, there is no way to execute the event again. After the second event stops firing, the event will still be executed again

For a richer set of throttling functions, see the JavaScript topic underscores

28. Write a const

Since the ES5 environment does not have the concept of a block, it is not possible to implement a const 100 percent. Instead, it has to be mounted to an object, either a global window or a custom object

Var _const = function __const (data, value) {var _const = function __const (data, value) {var _const = function __const (data, value) { // Use object.defineProperty to hijack the current Object and modify its property descriptor Object.defineProperty(window, data, {// Cannot enumerate enumerable: False, // No additional information can be added to any video system; false, get: function() {// Return the value of any video system; }, set: function(data) { if (data ! == value) {// An error is thrown when the current attribute is reassigned! throw new TypeError('Assignment to constant variable.'); } else { return value; }}})} // Test // define a const('a', 10); console.log(a); // 10 delete a; // false console.log(a); // we use enumerable because a const attribute doesn't exist in global either: For (let item in window) {if (item === 'a') {// Console. log(window[item]) is not executed because it is not enumerable; } } a = 20; Const obj _const('obj', {a: 1}); console.log(obj); Obj. B = 2; console.log(obj); obj = {}; / Failed to assign a new objectCopy the code

29. Implement a two-way binding

DefineProperty version

Object defineProperty is used to hijack the accessor of an Object. When the attribute value changes, we can get the change and then respond to the change. In Vue3.0, we can do similar operations through Proxy objects.

// const data = {text: 'default'}; const input = document.getElementById('input'); const span = document.getElementById('span'); DefineProperty (data, 'text', {enumerable: true, signals: Set (value) {input.value = value, sp.innerhtml = value; return value; Input. AddEventListener ('keyup', function(e) {data.text = e.target.value; })Copy the code

The proxy version

There are three main problems with object.defineProperty () :

  • Cannot listen for array changes
  • Each property of the object must be traversed
  • Nested objects must be traversed deeply

Proxy was formally added in the ES2015 specification, and it has the following advantages

  • Object: For the whole object, rather than an attribute of the object, so there is no need to iterate over keys. This solves the second problem with Object.defineProperty() above
  • Support array: Proxy does not need to overload the array method, eliminating many hacks, reducing the amount of code is equal to reducing the maintenance cost, and the standard is the best.
  • The second argument to Proxy can be intercepted in 13 ways, which is much richer than object.defineProperty ()
  • Proxy is a new standard that browser vendors are focusing on and optimizing for performance, while Object.defineProperty() is an old method that can benefit from the new version.
// const data = {text: 'default'}; const input = document.getElementById('input'); const span = document.getElementById('span'); Const handler = {set(target, key, Vlaue) {// target = target object // prop = set attribute // value = modified value target[key] = value; // Data changes --> modify view input.value = value; span.innerHTML = value; return value; Const proxy = new proxy (data, handler); Input.addeventlisener ('keyup', function(e) {proxy.text = e.target.value; });Copy the code

30. Lazy loading of images

Listen for scroll event method

Image, with an additional property to store the real image address:

  <img src="loading.gif" data-src="https://cdn.pixabay.com/photo/2015/09/09/16/05/forest-931706_1280.jpg" alt="">
  <img src="loading.gif" data-src="https://cdn.pixabay.com/photo/2014/08/01/00/08/pier-407252_1280.jpg" alt="">
  <img src="loading.gif" data-src="https://cdn.pixabay.com/photo/2014/12/15/17/16/pier-569314_1280.jpg" alt="">
  <img src="loading.gif" data-src="https://cdn.pixabay.com/photo/2010/12/13/10/09/abstract-2384_1280.jpg" alt="">
  <img src="loading.gif" data-src="https://cdn.pixabay.com/photo/2015/10/24/11/09/drop-of-water-1004250_1280.jpg"
Copy the code

ScrollTop determines whether the image is in the viewable area with the image offsetTop and the innerHeight of the window.

// The throttling function is guaranteed to trigger every 200ms
function throttle(func, wait = 200) {
    let timer = null;
    return function (. args) {
        if(! timer) { timer =setTimeout(() = > {
                timer = null;
                func.apply(this, args); }, wait); }}}// Get all the IMG tags
var imgs = document.getElementsByTagName("img");
// Store images that have already been loaded to avoid traversing from the first image each time
var n = 0;
// when the page is loaded, the image in the area is loaded
lazyload();

// Listen for page scroll events
window.addEventListener('scroll', throttle(lazyload, 200));
// lazy loading function
function lazyload() {
    // Height of visible area
    let visualHeight = window.innerHeight;
	// The height of the scrollbar from the top is compatible with Internet Explorer
    let scrollTop = document.documentElement.scrollTop || document.body.scrollTop
    // From the last unfinished img to the last img
    for (let i = n; i < imgs.length; i++) {
        // Within the window range
        if (img[i].offsetTop < visualHeight + scrollTop) {
            if (img[i].getAttribute('src') = = ="loading.gif") {
                // Replace SRC with data-src
                img[i].src = img[i].getAttribute("data-src");
            }
            n = i + 1; }}}Copy the code

IntersectionObserver

The IntersectionObserver interface (which is part of the IntersectionObserver API) provides a way to asynchronously observe the intersecting status of a target element with its ancestor element or top-level document viewport. Ancestor elements and viewports are called roots.

The Intersection Observer can invoke a callback as soon as an element is visible, without listening for scroll events. The callback is used to determine whether the element is visible.

// Get all the IMG tags
var imgs = document.getElementsByTagName("img");

// Verify that IntersectionObserver is available
if (IntersectionObserver) {
    let lazyloadObserver = new IntersectionObserver((entries) = > {
        entries.forEach((entry) = >{})})let lazyImgObserver = new IntersectionObserver((entries, observer) = > {
        entries.forEach((entry, index) = > {
            // lazy loading of images
            let lazyImg = entry.target;
            // If the element is visible
            if (entry.intersectionRatio > 0) {
                if (lazyImg.getAttribute("src") = = ="loading.gif") {
                    lazyImg.src = lazyImg.getAttribute("data-src");
                }
                // Stop listening for this element when the image is loadedlazyImgObserver.unobserve(lazyImg); }})})// observe listen on all IMG nodes
    for (let i = 0; i < imgs.length; i++) { lazyImgObserver.observe(imgs[i]); }}Copy the code

Interval random number generator

function random(m, n) {
	return Math.floor(Math.random() * (n - m)) + m;
	// return parseInt(Math.random() * (n - m)) + m;
}

for (let i = 0; i < 10; i++) {
	console.log(random(28, 45));
}
Copy the code

32. Print diamonds

function printDiamond(n) { for (let i = 0; i < n; i++) { for (let j = 0; j <= i; j++) { document.write("* "); } document.write("<br/>"); } for (let i = n - 2; i >= 0; i--) { for (let j = 0; j <= i; j++) { document.write("* "); } document.write("<br/>"); }}Copy the code

33. Handwritten parseInt

I thought it was very simple at the beginning, but I found it difficult to write it, or to write only with numbers and letters, 0x at the beginning of what I was afraid I would not be bald

function getNum(char) { if ('0' <= char && char <= '9') return Number(char); if ('a' <= char && char <= 'z') return char.charCodeAt() - 'a'.charCodeAt() + 10; if ('A' <= char && char <= 'Z') return char.charCodeAt() - 'A'.charCodeAt() + 10; } function _parseInt (STR, radix) {/ / string type let strType = Object. The prototype. The toString. Call (STR); If the type is not string or number, NaN if (strType! == '[object String]' && strType ! == '[object Number]') return NaN; // Radix = 0 null undefined if (! | | | | | | | | | | | | | } if (Object.prototype.toString.call(radix) ! == '[object Number]' || radix < 2 || radix > 36 || Math.floor(radix) < radix){ return NaN; } / / regular expressions, said several const re = / ^ [\ | \ +]? [0-9a-zA-Z]*(\.[0-9a-zA-Z]+)? /; STR = (STR + '').trim().match(re)[0]; if (! str.length) return NaN; let sign = "+"; / / deal with special case if (STR [0] = = = '+') {STR = STR. Slice (1); } if (str[0] === '-') { sign = "-"; str = str.slice(1); } if (str[0] === '.') { if (str[1]) { let num = getNum(str[1]); if (num < radix && sign === '+') return 0; if (num < radix && sign === '-') return -0; return NaN; } return NaN; } STR = str.split('.')[0]; if (! str.length) return NaN; let res = getNum(str[0]); if (res >= radix) return NaN; for (let i = 1; i < str.length; i++) { let num = getNum(str[i]); if (num >= radix) return sign === '+' ? res : -res; res = res * radix + num; } return sign === '+' ? res : -res; } console.log(_parseInt("F", 16)); console.log(_parseInt("17", 8)); console.log(_parseInt("015", 10)); The console. The log (_parseInt (15.99, 10)); The console. The log (_parseInt (" 15123 ", 10)); console.log(_parseInt("FXX123", 16)); console.log(_parseInt("1111", 2)); console.log(_parseInt("15 * 3", 10)); console.log(_parseInt("15e2", 10)); console.log(_parseInt("15px", 10)); console.log(_parseInt("12", 13)); console.log('-------------------------------'); console.log(_parseInt("Hello", 8)); console.log(_parseInt("546", 2)); console.log('-------------------------------'); console.log(_parseInt("-F", 16)); console.log(_parseInt("-0F", 16)); The console. The log (_parseInt (15.1, 10)); console.log(_parseInt(" -17", 8)); console.log(_parseInt(" -15", 10)); console.log(_parseInt("-1111", 2)); console.log(_parseInt("-15e1", 10)); console.log(_parseInt("-12", 13));Copy the code

Big guy’s version, can refer to, let me write on the spot certainly not

function compare(str, radix) { let code = str.toUpperCase().charCodeAt(0), num; if (radix >= 11 && radix <= 36) { if (code >= 65 && code <= 90) { num = code - 55; } else { num = code - 48; } } else { num = code - 48; } return num; } function isHex(first, str) { return first === '0' && str[1].toUpperCase() === 'X' } function _parseInt(str, radix) { str = String(str); if (typeof str ! == 'string') return NaN; str = str.trim(); let first = str[0], sign; / / handle the first character is' - '| |' + 'of the if (first = = =' - '| | first = = =' + ') {sign = STR [0]. str = str.slice(1); first = str[0]; } / / when radix does not exist or is less than 11, the first character for the digital if only (radix = = = undefined | | radix < 11) {if (isNaN (first)) return NaN; } let reg = /^(0+)/; Let reg2 = /^[0-9a-z]+/ I; let reg2 = /^[0-9a-z]+/ I; str = str.match(reg2)[0]; let len = str.length; // When there is no second argument or no number, For the second parameter assignment / / isNaN (' 0 x12) executes Number (' 0 x12) can be converted to a decimal if (radix = = = undefined | | isNaN (radix) | | radix = = = 0) {if (len === 1) return str; If (isHex(first, STR)) {if (sign === '-') {return Number(-str); } else { return Number(str); }} else {return Number(STR) = 0; }} else {radix = String(radix); | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | If (radix. Length > 1) {let twoR = radix[1]. if (radix[0] === '0' && twoR ! == 'X') radix = radix.replace(reg, ''); } // If radix is hexadecimal string type, it will be converted to decimal Number type; | | | | | | | | | | | | | | | | | | | | | | | | If (radix === 16&&ishex (first, STR)) return Number(STR); } else {// As long as the radix is a valid number but not in the correct interval, return NaN; STR = str.replace(reg, ''); if (str.length === 0) return 0; let strArr = str.split(''), numArr = [], result = 0, num; for (let i = 0; i < strArr.length; i++) { num = compare(strArr[i], radix); if (num < radix) { numArr.push(num); } else { break; } } let lenN = numArr.length; if (lenN > 0) { numArr.forEach(function (item, index) { result += item * Math.pow(radix, lenN - index - 1); }); } else {return first === '0'? 0 : NaN; } if (sign === '-') result = -result; return result; }Copy the code

34. The handwritten JSON. Stringify

Familiarize yourself with json.stringify

JSON.stringify(value[, replacer [, space]]) :Copy the code
  • Boolean | Number| StringThe type is automatically converted to the corresponding original value.
  • undefined, arbitrary functions andsymbolIs ignored (when appearing on a property value of a non-array object), or is converted tonull(when appearing in an array).
  • Non-enumerable properties are ignored if an object’s property value refers back to the object itself in some indirect way, i.e. a circular reference
  • Properties are also ignored if the value of an object’s property refers back to the object itself in some indirect way, i.e. a circular reference

The handwritten code is as follows

function jsonStringify(obj) {
    let type = typeof obj;
    if(type ! = ='object') {
        // Not string undefined and function
        if (/string|undefined|function/.test(type)) {
            obj = '"' + obj + '"';
        }
        return String(obj);
    }
    // JSON is an empty array
    let json = [];
    // Whether it is an array
    let arr = Array.isArray(obj);
    for (let key in obj) {
        // recursive call
        let value = jsonStringify(obj[key]);
        json.push((arr ? "" : '"' + key + '" :) + String(value));
    }
    return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}");
}

console.log(jsonStringify({x : 5})); // {"x":5}
console.log(jsonStringify([1."false".false])); // [1,"false",false]
console.log(jsonStringify({b: undefined})); // {"b":"undefined"}
Copy the code

Json.stringify’s shortcomings have been highlighted in a new way:

  • Properties of non-array objects are not guaranteed to appear in a serialized string in a particular order.
  • Booleans, numbers, and string wrapper objects are automatically converted to their original values during serialization.
  • undefined, arbitrary functions, and symbol values are ignored during serialization (when appearing in property values of non-array objects) or converted tonull(when appearing in an array). Function undefined returns undefined when converted separately, as inJSON.stringify(function(){}) or JSON.stringify(undefined).
  • Executing this method on objects that contain circular references (objects that refer to each other in an infinite loop) throws an error.
  • All properties with symbol as the property key are completely ignored, even thoughreplacerThey are mandatory in the parameter.
  • Date the Date is converted to a string by calling toJSON() (same as date.toisostring ()), so it is treated as a string.
  • Values and nulls in NaN and Infinity formats are treated as null.
  • Other types of objects, including Map/Set/WeakMap/WeakSet, serialize only enumerable properties.

##35. Write json. parse first familiarize yourself with the usage of json. parse

Json.parse (text[, reviver]) is used to parse JSON strings and construct JavaScript values or objects described by the string. Provides optional reviver functions to perform transformations (operations) on the obtained object before returning

Call eval directly

function jsonParse(opt) {
    return eval('(' + opt + ') ');
}

console.log(jsonParse(JSON.stringify({x : 5}))); // [object Object]: { x: 5}
console.log(jsonParse(JSON.stringify([1."false".false)));// [object Array]: [1, "false", false]
console.log(jsonParse(JSON.stringify({b: undefined}))); // [object Object]: {}
Copy the code

Avoid using eval() unnecessarily. Eval () is a dangerous function that executes code in executor rights. If the string code you run with Eval () is manipulated by a malicious party (someone with bad intentions), you could end up running malicious code on your web page/extension’s computer. It executes JS code and has XSS vulnerabilities.

If you only want to remember this method, you need to validate the json parameter.

var rx_one = /^[\],:{}\s]*$/;
var rx_two = / \ \? :["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
var rx_three = /"[^"\\\n\r]*"|true|false|null|-? \d+(? :\.\d*)? (? :[eE][+\-]? \d+)? /g;
var rx_four = / (? (: : ^ | |,)? :\s*\[)+/g;
if (
    rx_one.test(
        json
            .replace(rx_two, "@")
            .replace(rx_three, "]")
            .replace(rx_four, ""))) {var obj = eval("(" +json + ")");
}
Copy the code

Call Function core: Function has the same string argument property as eval

var func = new Function(arg1, arg2, ... , functionBody);Copy the code

In a real-world application of converting JSON, this is all you need to do

var jsonStr = '{ "age": 20, "name": "jack" }'
var json = (new Function('return ' + jsonStr))();
Copy the code

Eval and Function are both useful for dynamically compiling JS code, but are not recommended for real-world programming

The test results are as follows

let jsonStr = JSON.stringify({x : 5});
console.log((new Function('return ' + jsonStr))()); // [object Object]: {x: 5}
jsonStr = JSON.stringify([1."false".false]);
console.log((new Function('return ' + jsonStr))()); // [object Array]: [1, "false", false]
jsonStr = JSON.stringify({b: undefined});
console.log((new Function('return ' + jsonStr))()); // [object Object]: {}
Copy the code

Eval and Function are both useful for dynamically compiling JS code, but are not recommended for real-world programming.

The third and fourth methods involve complicated recursion and state machine related principles. For details, see json. parse

Parse the URL Params as an object

Parse all parameters of an arbitrary URL as Object as fully and correctly as possible. Note that the processing requirements of boundary conditions are as follows: • 1. Duplicate keys are assembled into arrays • 2. Those that can be converted to numbers are converted to numeric types • 3. • 4. The key convention with no specified value is true

let url = 'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';
parseParam(url);
/* Result {user: 'anonymous', id: [123, 456], // Duplicate keys to array, can be converted to number type city: 'Beijing ', // Chinese decoding needs to be enabled: True, // The value key convention is not specified to be true} */
Copy the code

The specific implementation code idea is as follows, the URL is segmented

function parseParam(url) {
    / / will be? I'm going to pull out the string
    const paramsStr = url.split('? ') [1];
    // Split the string with & and store it in the array
    const paramsArr = paramsStr.split('&');
    // Save params to the object
    let paramsObj = {};
    for (let i = 0; i < paramsArr.length; i++) {
        // Split key and value
        let [key, value] = paramsArr[i].split('=');
        // Handle arguments that have no value. The convention is true
        if(! value) value =true;
        // Chinese decoding
        value = decodeURIComponent(value);
        // Convert to numeric type, must be put after Chinese decoding
        if (/^\d+(\.\d+)? $/.test(value)) value = Number(value);
        // Handle duplicate keys and assemble them into arrays
        if (paramsObj[key]) {
            // The value is an array
            paramsObj[key] = Array.isArray(paramsObj[key]) ? [...paramsObj[key], value] : [paramsObj[key], value];
        } else{ paramsObj[key] = value; }}return paramsObj;
}
let url = 'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';
console.log(parseParam(url));
Copy the code

You can also use regular expression segmentation, which is the big guy version found online

function parseParam(url) {
 	/ / will be? I'm going to pull out the string
    const paramsStr = /. + \? (. +) $/.exec(url)[1];
    // Split the string with & and store it in the array
    const paramsArr = paramsStr.split('&');
    let paramsObj = {};
    // Save params to the object
    paramsArr.forEach(param= > {
     	// Handle arguments with value
        if (/ = /.test(param)) {
        	// Split key and value
        	let [key, val] = param.split('=');
        	// Recursive call decoding
        	val = decodeURIComponent(val);
        	// Determine whether to convert to a number
        	val = /^\d+$/.test(val) ? parseFloat(val) : val; 

			// Add a value if the object has a key
            if (paramsObj.hasOwnProperty(key)) {
            	paramsObj[key] = [].concat(paramsObj[key], val);
            } else {
            	// If the object does not have this key, create the key and set the valueparamsObj[key] = val; }}else {
        	// Handle arguments without value
        	paramsObj[param] = true; }})return paramsObj;
}
let url = 'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';
console.log(parseParam(url));
Copy the code

37. Template engine implementation

Render data from the data object into the Template template

Let template = 'I am {{name}}, age {{age}}, gender {{sex}}'; Let data = {name: 'name ', age: 18} render(template, data); // I am name, age 18, gender undefinedCopy the code

Follow the big guy’s handwritten code

Const reg = /\{\{(\w+)\}\}/; function render(template, data) {// const reg = /\{\{(\w+)\}\}/; Const name = reg.exec(template)[1]; if (reg.test(template)) {const name = reg.exec(template)[1]; Replace (reg, data[name]); // Render the first template string template = template.replace(reg, data[name]); Render (template, data); // Render (template, data); } // If the template has no template string, return template; } let the template = 'I am {{name}}, {{age}}, age sex {{sex}}'; Let data = {name: 'name ', age: 18} console.log(render(template, data)); // I am name, age 18, gender undefinedCopy the code

Recursion becomes iteration

Const reg = /\{\{(\w+)\}\}/; function render(template, data) {// const reg = /\{\{(\w+)\}\}/; While (reg.test(template)) {const name = reg.exec(template)[1]; const name = reg.exec(template)[1]; const name = reg.exec(template)[1]; Replace (reg, data[name]); // Render the first template string template = template.replace(reg, data[name]); } // If the template has no template string, return template; } let the template = 'I am {{name}}, {{age}}, age sex {{sex}}'; Let data = {name: 'name ', age: 18} console.log(render(template, data)); // I am name, age 18, gender undefinedCopy the code

Underscore (1) * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

38. Hump naming – line conversion

Center line to hump

// Replace the letters after - with uppercase letters
function fn(str) {
	return str.replace(/-\w/g.function (v) {
		// Uppercase
		return v.slice(1).toUpperCase(); })}let s1 = "get-element-by-id"; // Convert to getElementById
console.log(fn(s1));
Copy the code

Simplify the code

// Replace the letters after - with uppercase letters
function fn(str) {
	return str.replace(/-\w/g.v= > v[1].toUpperCase());
}
let s1 = "get-element-by-id"; // Convert to getElementById
console.log(fn(s1));
Copy the code

or

// Replace the letters after - with uppercase letters
function fn(str) {
	return str.replace(/-(\w)/g.(v1, v2) = > v2.toUpperCase());
}
let s1 = "get-element-by-id"; // Convert to getElementById
console.log(fn(s1));
Copy the code

Hump to center line

// Replace uppercase letters with - and lowercase letters
function fn(str) {
	return str.replace(/[A-Z]/g.v= > The '-' + v.toLowerCase());
}
let s2 = "getElementById"; // convert to get-element-by-id
console.log(fn(s2));
Copy the code

Expand to 4 modes

Common naming styles in programming languages are as follows: 1. Uppercase all letters 2. The first letter of the word should start with a lowercase letter, and the rest should start with a uppercase letter. Words are all lowercase, connected by a minus sign

Design and implement a caseTransform function so that a string STR can be easily converted into four forms, and the four forms are returned as a single string by concatenating Spaces. For convenience, we assume that the input string matches all four forms

Input description:

PascalCaseTest output:

PascalCaseTest PascalCaseTest pascal_case_test Pascal-case-test Identifies the mode and concatenates it

function caseTransform(s) {
	let list = new Array(4);
	if (s.indexOf('_') != -1) {
		// The third case
		/ / itself
		list[2] = s;
		/ / cutting
		let arr = s.split('_');
		// The fourth mode
		list[3] = arr.join(The '-');
		// The first mode
		list[0] = arr.map((item) = > item[0].toUpperCase() + item.slice(1)).join(' ');
		// The second mode
		list[1] = list[0] [0].toLowerCase() + list[0].slice(1);
	} else if (s.indexOf(The '-') != -1) {
		// The fourth case
		/ / itself
		list[3] = s;
		/ / cutting
		let arr = s.split(The '-');
		// The third mode
		list[2] = arr.join('_');
		// The first mode
		list[0] = arr.map((item) = > item[0].toUpperCase() + item.slice(1)).join(' ');
		// The second mode
		list[1] = list[0] [0].toLowerCase() + list[0].slice(1);
	} else if (s[0] > ='A' && s[0] < ='Z') {
		// The first case
		list[0] = s;
		// The second mode
		list[1] = s[0].toLowerCase() + s.slice(1);
		// The third mode
		list[2] = list[1].replace(/[A-Z]/g.function(x) {
            return '_' + x[0].toLowerCase();
        });
		// The fourth mode
		list[3] = list[2].replace(/_/g.The '-');
	} else {
		// The second mode
		list[1] = s;
		// The first mode
		list[0] = s[0].toUpperCase() + s.slice(1);
		// The third mode
		list[2] = s.replace(/[A-Z]/g.function(x) {
            return '_' + x[0].toLowerCase();
        });
		// The fourth mode
		list[3] = list[2].replace(/_/g.The '-');
	}
	return(list);
}
let str = 'PascalCaseTest';
console.log(caseTransform(str).join(' '));
Copy the code

39. Find the maximum number of characters in a string

Sort + regular statistics of a single character

1. Arrange the data in a certain order

2. Use regex to match data

backreferences

() Correlation matches are stored in a temporary buffer, and each child match captured follows the regular pattern from left to right

The order of occurrence is stored. Buffer numbers start at 1, up to 99 captured subexpression,

Each buffer can be represented by \n, where n is a one – or two-digit decimal number identifying a particular buffer. Such as:

\1 Specifies the first child match, specifying that the second part of the regular expression is a reference to the previously captured child match

That is, the second match is exactly matched by the parenthesis expression.

The replace() method is used to replace some characters in a string with other characters, or to replace a substring that matches a regular expression.

Grammar:

​ stringObject.replace(regexp/substr,replacement)

Parameters:

Regexp /substr: RegEXP object specifying the substring or pattern to replace

Replacement: Specifies a function that replaces the text or generates the replacement text.

It can be a string or a function

String: Each match is replaced by a string

Function:

Parameter characteristics:

The first argument: the string that matches the pattern

Other parameters: Strings that the subexpression matches in the pattern, which can be 0 or more

The next argument, an integer, declares to match the position that occurs in stringObject

The last parameter: stringObject itself

let str = "abcabcabcbbccccc";
let num = 0;
let char = ' ';
// Arrange them in a certain order
str = str.split(' ').sort().join(' ');
// "aaabbbbbcccccccc"

// Define a regular expression
let re = /(\w)\1+/g;
str.replace(re, ($0, $1) = > {
	if (num < $0.length) {
		// num is always the one with the largest number of stores
		num = $0.length;
		char = $1;
	} else if (num === $0.length){
		if (Array.isArray(char)) {
			char.push($1);
		} else {
			char = [char, $1]; }}})console.log('the most characters are${char}Appeared,${num}Time `);
// The most common character is c, which occurs 8 times
Copy the code

Hash table counts the number of single characters

let str = "abcabcabcbbccccc";
let num = 0;
let char = ' ';
/ / a hash table
let obj = {};
// Arrange them in a certain order
for (let i = 0; i < str.length; i++) {
    let char = str[i];
    if (obj[char]) {
        // The number increases by 1
        obj[char]++;
    } else {
        // If it is the first time, count as 1
        obj[char] = 1; }}// The output is a complete object, recording each character and the number of occurrences
console.log(obj);
/* a: 3 b: 5 c: 8 */

for (let key in obj) {
    if (num < obj[key]) {
		num = obj[key];
		char = key;
	} else if (num === obj[key]){
		if (Array.isArray(char)) {
			char.push(key);
		} else{ char = [char, key]; }}}/* a: 3 b: 5 c: 8 */
console.log('the most characters are${char}Appeared,${num}Time `);
Copy the code