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:
- check
map
Is there any cloned object in - Yes. – Straight back
- None – takes the current object as
key
, clone the object asvalue
For 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 asundefined
And theundefined
和null
Will be processed as an empty string.
ES6 explicitly converts the void toundefined
.
entries()
,keys()
,values()
,find()
和findIndex()
Will process the empty space intoundefined
.for... of
The 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.from
Method 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:
-
- Defines an array with the last element of the array as its reference point.
-
- 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.
-
- Base point shifted one to the left.
-
- 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 browser
resize
- 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:
- The first event is executed immediately, and the second event is executed for the first time in n seconds
- 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| String
The type is automatically converted to the corresponding original value.undefined
, arbitrary functions andsymbol
Is 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(){})
orJSON.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 though
replacer
They 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