JS handwriting topic summary

Summarize the common handwritten JavaScript questions in the interview, such as call, apply, bind, promise, etc. Only if you can skillfully master the common handwritten JavaScript questions in the interview, you can make the interviewer’s eyes shine without saying much and directly answer the questions.

1. Implement call, apply and bind

1. Implement Call

Function: calls a function with the specified this value and one or more arguments;

  • The first parameter is zeronullorundefinedWhen,thisPointing to a global objectwindow, an auto-wrapped object that points to the original value, such asString, Number, Boolean;
  • To avoid function names and context (context) attribute conflict, useSymbolType as unique value;
  • Function as the context passed in (context) property execution;
  • After the function completes, delete the attribute;
  • Returns the execution result;
// ES5
Function.prototype.call2 = function(context) {
  if (typeof this! = ='function') {
    throw new TypeError('Type Error');
  }
  var context = context || window;
  // Bind this to a method on an object
  context.fn = this;
  var args = [];
  // Store function variable parameters
  for (var i=1; i<arguments.length; i++) {
    args.push('arguments[' + i + '] ');
  }
  // Execute the method just bound on the context
  var result = eval('context.fn(' + args + ') ');
  // Delete the method you just created
  delete context.fn;
  // Returns the return value of the function call
  return result;
}

// ES6 (recommended)
// Pass parameters from an array to one by one instead of... Arguments can also be used instead of extension operators
Function.prototype.call2 = function (context, ... args) {
  // You can also use ES6 to set default parameters for parameters
  context = context || window;
  args = args ? args : [];
  // Add a unique attribute to the context to avoid overwriting the original attribute
  const key = Symbol(a); context[key] =this;
  // Call the function with an implicit binding
  constresult = context[key](... args);// Delete the added attribute
  delete context[key];
  // Returns the return value of the function call
  return result;
}

// Test it
var value = 2;
var obj = {
  value: 1
}
function bar(name, age) {
  console.log(this.value);
  return {
    value: this.value,
    name: name,
    age: age
  }
}
console.log(bar.call2(null)); / / 2
console.log(bar.call2(obj, 'kevin'.18)); / / 1
Copy the code

2. Implement Apply

Just like call, the only difference is that call passes in an unlimited number of arguments, while apply passes in an array.

  • Former part andcallThe same;
  • The second argument may not be passed, but the type must be array or array-like.
// ES5
Function.prototype.apply2 = function(context,arr) {
  if (typeof this! = ='function') {
    throw new TypeError('Type Error');
  }
  var context = context || window;
  context.fn = this;
  if(! arr) {var result = context.fn();
  } else {
    var args = [];
    for (var i=0; i<arr.length; i++) {
      args.push('arr[' + i + '] ');
    }
    var result = eval('context.fn(' + args + ') ');
  }
  delete context.fn;
  return result;
}
// ES6 (recommended)
Function.prototype.apply2 = function (context, args) {
  // You can also use ES6 to set default parameters for parameters
  context = context || window;
  args = args ? args : [];
  // Add a unique attribute to the context to avoid overwriting the original attribute
  const key = Symbol(a); context[key] =this;
  // Call the function with an implicit binding
  constresult = context[key](... args);// Delete the added attribute
  delete context[key];
  // Returns the return value of the function call
  return result;
}

// Test it
var value = 2;
var obj = {
  value: 1
}
function bar(name, age) {
  console.log(this.value);
  return {
    value: this.value,
    name: name,
    age: age
  }
}
console.log(bar.apply2(null)); / / 2
console.log(bar.apply2(obj, 'kevin'.18)); / / 1
Copy the code

3. Bind

The bind method creates a new function. When bind() is called, this of the new function is specified as the first argument to bind(), and the remaining arguments are used as arguments to the new function.

Need to consider:

  • bind()In addition tothisMore than one parameter can be passed;
  • bindA new function created may pass in multiple arguments;
  • New functions may be called as constructors;
  • A function may have a return value;

Implementation method:

  • bindMethod does not execute immediately and needs to return a function to be executed; (closures)
  • Implementing scoped binding (apply);
  • Parameter passing (applyArray pass parameter);
  • When the asThe constructorWhen carrying onPrototype inheritance;
// ES5
Function.prototype.bind = function (oThis) {
  var aArgs = Array.prototype.slice.call(arguments.1);
  var fToBind = this;
  var fNOP = function () {};
  var fBound = function () {
    fBound.prototype = this instanceof fNOP ? new fNOP() : fBound.prototype;
    return fToBind.apply(this instanceof fNOP ? this : oThis || this, aArgs )
  }   
  if( this.prototype ) {
    fNOP.prototype = this.prototype;
  }
  return fBound;
}

// ES6 (recommended)
Function.prototype.bind2 = function (context, ... args) {
  const fn = this;
  args = args ? args : [];
  return function newFn(. newFnArgs) {
    // determine if it is used as a constructor
    if (this instanceof newFn) {
      return newfn(... args, ... newFnArgs) }// When executed as a normal function, use apply to execute the function
    return fn.apply(context, [...args,...newFnArgs])
  }
}

// Test it
var value = 2;
var obj = {
  value: 1
}
function bar(name, age) {
  console.log(this.value);
  return {
    value: this.value,
    name: name,
    age: age
  }
}
const b = bar.bind2(null.'kevin'); / / 2
b(18) / / 2
const b_ = bar.bind2(obj, 'kevin');
b_(18); / / 1
Copy the code

2. Implement new

The new operator is used to create instances of user-defined object types or built-in objects with constructors.

  • Create a new object;
  • Sets the prototype of the object to functionprototypeObject;
  • Let the function of thethisPoint to the object and execute the constructor code (add attributes to the new object);
  • Determine the return value type of the function. If it is a value type, return the created object. If it is a reference type, return the object of the reference type.
// ES5 (recommended)
function newFunc() {
  // Create a new object
  var obj = new Object(a);// Extract the first argument, the constructor
  var Constructor = [].shift.call(arguments);
  // Set the prototype of the object to the function's 'prototype' object.
  obj.__proto__ = Constructor.prototype;
  // Make the function 'this' point to the object and execute the constructor code (add attributes to the new object)
  var ret = Constructor.apply(obj, arguments);
  // Determine the return type of the function, if it is a value type, return the newly created object. If it is a reference type, an object of that reference type is returned.
  return typeof ret === 'object' ? ret : obj;
}

// ES6 (recommended)
function newFunc(. args) {
  const obj = Object.create({});
  const Constructor = [].shift.call(args);
  obj.__proto__ = Constructor.prototype;
  const result = Constructor.apply(obj, args);
  return typeof result === 'object' ? result : obj;
}

function F(name, age) {
  this.name = name;
  this.age = age
}
const newF = newFunc(F, 'dell'.18);
console.log(newF.name, newF.age); // dell 18
Copy the code

3. Seven inheritance modes

1. Prototype chain inheritance

Advantages:

  • The ability to inherit attributes and methods from the parent class;

Disadvantages:

  • Problem 1: Included in the prototypeReference type attributeWill be shared by all instances (cannot be implementedMultiple inheritance);
  • Question 2:Subclass instantiationCannot pass an argument to the parent constructor.
  • Problem 3: Unable to obtain properties and methods on the prototype of a subclass after instantiation;
function Parent() {
  this.name = 'parent1';
  this.play = [1.2.3];
  this.add = function() {
    return {
      name: this.name,
      play: [1.2.3]
    }
  }
}
Parent.prototype.getName = function () {
  return this.name;
}
function Child() {
  this.type = 'child2';
  this.add2 = function() {
    return this.type;
  }
}
Child.prototype.add1 = function() {
  return this.type;
}
Child.prototype.dell = 'dell';
// Prototype chain inheritance
Child.prototype = new Parent();
// Create an instance
var c = new Child();
console.log(c.name, c.type, c.add(), c.add2(), c.getName()); // All of them are available

// Cannot read properties and methods on its own prototype
console.log(c.add1()); // Cannot read
console.log(c.dell);

// Multiple inheritance cannot be used. You can modify attributes on the parent stereotype, but if the attribute is a reference attribute, it will share the same instance, as follows:
var child1 = new Child();
var child2 = new Child();
child1.name = 'dell';
child1.play[2] = 56;
console.log(child1.name); // dell
console.log(child2.name); // parent1
console.log(child1.play); / /,2,56 [1]
console.log(child2.play); / /,2,56 [1]
Copy the code

Constructor inheritance

Advantages:

  • Solve the problem of prototype chain inheritance (1) can not achieve multiple inheritance; Class instantiation does not take arguments to the constructor.

Disadvantages:

  • Only the attributes and methods of the parent class can be inherited, but not the attributes and methods of the parent class prototype.
  • Because methods are defined in constructors, they are created each time a subclass is instantiated;
function Parent(name) {
  this.name = name;
  this.age = '18';
  this.play = [1.2.3];
  this.getAge = function() {
    return this.age;
  }
}
Parent.prototype.lee = 'lee';
Parent.prototype.getName = function () {
  return this.name;
}
function Child(name) {
  / / the refs
  Parent.call(this, name); // Inherits the parent class
  this.type = 'child2';
  this.getDell_ = function() {
    return this.age;
  }
}
Child.prototype.getDell = function() {
  return this.age;
}
// Create an instance
var c1 = new Child('name');
var c2 = new Child('name');

// Get subclass prototype attributes and methods
console.log(c1.name, c1.age, c1.play, c1.getAge(), c1.getDell_(), c1.getDell()) // parent1 18 18 18 18

// Multiple inheritance can be implemented, i.e. no longer sharing the same instance
c1.play[1] = 99
console.log(c1.play) / / [1, 99, 3]
console.log(c2.play) / / [1, 2, 3]

// Can't get properties and methods on the superclass prototype
console.log(c1.lee) // undefined
console.log(c1.getName()) / / an error
Copy the code

3. Combinatorial inheritance

Advantages:

  • It solves the problem of using prototype chain inheritance or constructor inheritance alone.

Disadvantages:

  • Because composite inheritance is a combination of stereotype chain inheritance and constructor inheritance, the parent class is called once in both stereotype chain inheritance and constructor inheritance, so the parent class is called twice.
function Parent() {
  console.log('Two calls')
  this.name = 'parent1';
  this.age = '18';
  this.play = [1.2.3];
  this.getAge = function() {
    return this.age
  }
}
Parent.prototype.lee = 'lee'
Parent.prototype.getName = function () {
  return this.name;
}
function Child() {
  Parent.call(this); // Call Parent a second time
  this.type = 'child2';
}
Child.prototype.getDell = function() {
  return this.age
}
Child.prototype = new Parent(); // Call Parent for the first time
Child.prototype.constructor = Child; // Manually hang constructor to avoid losing access to Parent
var c = new Child();
console.log(c.name, c.age, c.getAge()) // parent1 18 18
console.log(c.lee) // lee
console.log(c.getName()) // parent1
Copy the code

4. Original type inheritance

Advantages:

  • The ability to inherit attributes and methods from the parent class;

Disadvantages:

  • Does not solve the reference type sharing problem (multiple inheritance cannot be implemented);
var Parent = {
  name: 'dell'.arr: [18.19.20].getName: function() {
    return this.name
  }
}
let p1 = Object.create(Parent);
p1.name = 'tom'
p1.arr.push('lee')
let p2 = Object.create(Parent);
p2.name = 'lee';
p2.arr.push('dell')
console.log(p1.name, p1.arr, p1.getName())// tom [18, 19, 20, "lee", "dell"] tom
console.log(p2.name, p2.arr, p2.getName()) // lee [18, 19, 20, "lee", "dell"] lee
// Advantages: Can inherit properties and methods Disadvantages: does not solve the reference problem, create the same reference
Copy the code

Parasitic inheritance

Advantages:

  • The ability to inherit attributes and methods from the parent class;

Disadvantages:

  • Does not solve the reference type sharing problem (multiple inheritance cannot be implemented);
var Parent = {
  name: 'dell'.arr: [18.19.20].getName: function() {
    return this.name
  }
}
let p1 = Object.create(Parent);
p1.getArr = function() {
  return this.name
}
console.log(p1.name, p1.arr, p1.getName(), p1.getArr()) // dell [18, 19, 20] dell dell
Copy the code

6. Combinatorial parasitic inheritance

Advantages:

  • Object. Create (Parent. Prototype); Object. Create (Parent. Prototype)

Disadvantages:

  • ES6 class inheritance is recommended.
function Parent() {
  console.log('Executed once, not twice')
  this.name = 'dell'
  this.age = 18
  this.getName = function() {
    return this.name
  }
}
Parent.prototype.getAge = function() {
  return this.age;
}
function Child() {
  Parent.call(this);
  this.type = 'type'
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
// Create an instance
var p = new Child();
console.log(p.name, p.age, p.getName()) // dell 18 dell
console.log(p.getAge()) / / 18
console.log(p.type) // type
// console.log(c.getdell ()) // why can't access the methods on the subclass prototype
Copy the code

7. ES6 inheritance mode

// class is equivalent to the ES5 constructor;
// use the protopyte property of the class to define a method.
// All methods defined in class are not enumerable;
// only methods can be defined in class.
// Strict mode is the default in both class and method;
// ES5 constructor = implicit;
class People {
  constructor(name='dai',age='27') {
    this.name = name;
    this.age = age;
  }
  eat() {
    console.log(`The ${this.name} The ${this.age} eat food`); }}// Inherits the parent class
class Woman extends People { 
   constructor(name = 'dai',age = '27') { 
     // Inherits the superclass attributes
     super(name, age); 
   } 
    eat() { 
     // Inherits the parent method
      super.eat()
    } 
} 
let wonmanObj = new Woman('dai'); 
wonmanObj.eat();

// ES5 inherits the instance object of the subclass and then adds the methods of the Parent class to this (parent.apply (this));
// ES6 inheritance uses the keyword super to create an instance object of the parent class this, and finally to modify this in the subclass class;
Copy the code

4. Throttling and anti-shaking

1. The image stabilization (debounce)

  • If a high-frequency event is triggered, the event is executed once after n seconds. If the event is triggered again within n seconds, the time is recalculated.
  • Scenario: Input Search box searches for input;
<input oninput="debounceInput(event)" />

function debounce(fn, delay) {
  var timeout = null;
  return function() {
    // Clear timer and recalculate time
    clearTimeout(timeout);
    // Execute the function when the time is up
    timeout = setTimeout(() = > {
      fn.apply(this.arguments)
    }, delay)
  }
}

function onInput(event) {
  if (event.target.value) {
    console.log(event.target.value); }}let debounceInput = debounce(onInput, 300)
// Purpose: Search for input boxes
Copy the code

2. The throttle

  • After the high-frequency event is triggered, it is executed only once within n seconds. Throttling is the execution frequency of dilution function.
  • Scenario: long list scrollthrottling, resize;

2.1 the timestamp

  • First throttle, the first time to execute immediately, but after stopping the start, can not execute again;
// Timestamp implementation
function throttle(fn, delay) {
  var last = 0;
  return function () {
    var now = Date.now();
    if (now - last >= delay) {
      last = now;
      fn.apply(this.arguments)}}}Copy the code

2.2 setTimeout

  • Tail throttling does not execute the function immediately, but after delay;
// The timer is implemented
// The delay timer will be executed for the last time after the last stop.
function throttle(fn, delay) {
  var timer = null;
  return function() {
    if(! timer) { timer =setTimeout(() = > {
        fn.apply(this.arguments);
        timer = null;
      }, delay)
    }
  }
}
Copy the code

2.3 Timestamp + setTimeout

  • The first and tail throttling method is realized
// Time stamp + timer implementation
function throttle(fn, delay){
  let timer = null;
  let startTime = 0;
  return function() {
    let curTime = Date.now();
    let remaining = delay - (curTime - startTime);
    clearTimeout(timer);
    if (remaining <= 0) {
      fn.aplly(this.arguments);
      startTime = Date.now();
    } else {
      timer = setTimeout(() = > {
        fn.aplly(this.arguments);
        startTime = Date.now();
      }, remaining)
    }
  }
}
Copy the code

5, The Promise series handwritten

1. Write basic Promises by hand

class Prom {
  static resolve (value) {
    if (value && value.then) {
      return value
    }
    return new Prom(resolve= > resolve(value))
  }

  constructor (fn) {
    this.value = undefined
    this.reason = undefined
    this.status = 'PENDING'

    // Maintain a resolve/pending function queue
    this.resolveFns = []
    this.rejectFns = []

    const resolve = (value) = > {
      // Notice the setTimeout here
      setTimeout(() = > {
        this.status = 'RESOLVED'
        this.value = value
        this.resolveFns.forEach(({ fn, resolve: res, reject: rej }) = > res(fn(value)))
      })
    }

    const reject = (e) = > {
      setTimeout(() = > {
        this.status = 'REJECTED'
        this.reason = e
        this.rejectFns.forEach(({ fn, resolve: res, reject: rej }) = > rej(fn(e)))
      })
    }

    fn(resolve, reject)
  }

  then (fn) {
    if (this.status === 'RESOLVED') {
      const result = fn(this.value)
      // Return a Promise
      // If resolved, execute
      return Prom.resolve(result)
    }
    if (this.status === 'PENDING') {
      // Return a Promise
      return new Prom((resolve, reject) = > {
        // In the queue, resolved
        this.resolveFns.push({ fn, resolve, reject })
      })
    }
  }

  catch (fn) {
    if (this.status === 'REJECTED') {
      const result = fn(this.value)
      return Prom.resolve(result)
    }
    if (this.status === 'PENDING') {
      return new Prom((resolve, reject) = > {
        this.rejectFns.push({ fn, resolve, reject })
      })
    }
  }
}

Prom.resolve(10).then(o= > o * 10).then(o= > o + 10).then(o= > {
    console.log(o)
})

return new Prom((resolve, reject) = > {
    reject('Error')
}).catch(e= > {
    console.log('Error', e)
})
Copy the code

2. Promise.resolve

  • Promise.resolve(value)Any value can be converted to a value ofvalueThe state isfulfilledPromise, but if the passed value itself isPromiseIt is returned as is;
Promise.resolve = (param) = > {
  if(param instanceof Promise) return param;
  return new Promise((resolve, reject) = > {
    if(param && param.then && typeof param.then === 'function') {
      // A successful param state calls resolve, which changes the state of the new Promise to successful and vice versa
      param.then(resolve, reject);
    } else{ resolve(param); }})}Copy the code

3. Promise.reject

  • andPromise.resolve()A similar,Promise.reject()It instantiates onerejectedThe state of thePromise. But with thePromise.resolve()The difference is, if you givePromise.reject()Pass aPromiseObject, the object becomes newPromiseThe value of the;
Promise.reject = function(reason) {
  return new Promise((resolve, reject) = > reject(reason))
}
Copy the code

4. Promise.prototype.finally

  • Whether you succeed or fail, you will get therefinally, andfinallyAfter that, you can continuethen. And it passes the value exactly as it wasthen;
Promise.prototype.finally = function (callback) {
  return this.then((value) = > {
    return Promise.resolve(callback()).then(() = > {
      return value;
    });
  }, (err) = > {
    return Promise.resolve(callback()).then(() = > {
      throw err;
    });
  });
}

Copy the code

5. Promise.all

  • Incoming allPromsieAre allfulfilled, returns the values of the values in the statefulfilledThe newPromise;
  • As long as there is onePromiserejected, the returnrejectedState of the newPromsie, and its value is the firstrejectedPromiseThe value of the;
  • As long as there is onePromisepending, returns onependingState of the newPromise;
function PromiseAll(promiseArray) {
  return new Promise((resolve, reject) = > {
    if (!Array.isArray(promiseArray)) {
      return reject(new Error('The argument passed must be an array! '));
    }
    let arr = [];
    const len = promiseArray.length;
    let counter = 0;
    for (let i=0; i<len; i++) {
      Promise.resolve(promiseArray[i]).then((res) = > {
        // arr.push(res);
        counter++;
        arr[i] = res;
        if (counter === len) {
          resolve(arr);
        }
      }).catch(err= > reject(err))
    }
  })
}
const pro1 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    resolve('1')},1000)})const pro2 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    resolve('2')},2000)})const pro3 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    resolve('3')},3000)})const promise = PromiseAll([pro1, pro2, pro3]).then((res) = > {
  console.log(res)
}).catch(err= > console.log(err))
Copy the code

6. Promise.race

  • Promise.raceReturns the first of all iterable instancesfulfilledrejectedThe new instance is wrapped after the instance.
Promise.race = function(promiseArr) {
  return new Promise((resolve, reject) = > {
    for (let i=0; i<promiseArr.length; i++) {
      Promise.resolve(promiseArr[i]).then(res= > {
        resolve(res)
      }).catch(err= > reject(err))
    }
  })
}
Copy the code

7. Promise.allSettled

  • Promise.allSettled()Method returns apromisethepromiseIn all givenpromiseParsed or rejected, and each object describes eachpromiseResults.
// The format is as follows
Promise.allSettled([
  Promise.resolve('a'),
  Promise.reject('b'),
])
.then(arr= > {
  console.log(res);
  / / /
  // { status: 'fulfilled', value: 'a' },
  // { status: 'rejected', reason: 'b' },
  // ]
});
Copy the code
function PromiseAllSettled(arr) {
  return new Promise((resolve, reject) = > {
    if (!Array.isArray(arr)) {
      throw new Error('Must be an array')}let count = 0;
    let len = arr.length;
    let array = [];
    for (let i=0; i<len; i++) {
      Promise.resolve(arr[i]).then((item) = > {
        count++;
        array[i] = {status: 'fulfilled'.value: item};
        if (count >= len) {
          resolve(array)
        }
      }).catch((reason) = > {
        count++;
        array[i] = {status: 'rejected'.value: reason};
        if (count >= len) {
          resolve(array)
        }
      })
    }
  })
}

const pro1 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    resolve('1')},1000)})const pro2 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    reject('2')},2000)})const pro3 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    resolve('3')},3000)})const promise = PromiseAllSettled([pro1, pro2, pro3]).then((res) = > {
  console.log(res)
}).catch(err= > console.log(err))
Copy the code

8. Promise.any

  • The method takes a set of Promise instances as parameters and returns them wrapped as a new Promise instance. As long as one parameter instance becomes a depressing state, the packaging instance will become a depressing state. If all parameter instances become the Rejected state, the wrapper instance becomes the Rejected state.

  • Promise.any() is like the promise.race () method, except that promise.any () does not end when a Promise changes to the Rejected state. You must wait until all the parameters Promise become rejected.

// Promise.any
function PromiseAny(arr) {
  return new Promise((resolve, reject) = > {
    if (!Array.isArray(arr)) {
      throw new Error('Must pass in an array')}let len = arr.length;
    let count = 0;
    let result = [];
    for (let i=0; i<len; i++) {
      Promise.resolve(arr[i]).then(res= > {
        resolve(res);
      }).catch(err= > {
        result[i] = err;
        count++;
        if (count === len) {
          reject(`AggregateError: All promises were rejected`}})}})}/ / test
const pro1 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    reject('1')},1000)})const pro2 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    reject('2')},2000)})const pro3 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    reject('3')},3000)})const promise = PromiseAny([pro1, pro2, pro3]).then((res) = > {
  console.log(res)
}).catch(err= > console.log(err))
Copy the code

6. Ajax implementation

  • usePromiseencapsulationAjax;
function ajax(method, url, body, headers) {
  // Return a Promise
  return new Promise((resolve, reject) = > {
    // Initialize an XMLHttpRequest() object
    let xhr = new XMLHttpRequest();
    // Call the open method and pass in three parameters: request method, URL, synchronous asynchrony
    xhr.open(methods, url, true);
    // Loop through the request header and set the request header setRequestHeader()
    for(let key in headers) {
      if (headers.hasOwnProperty(key)) {
        xhr.setRequestHeader(key, headers[key])
      }
    }
    // Listen for onreadyStatechange () and return responseText if readyState === 4
    xhr.onreadystatechange(() = > {
      if(xhr.readyState == 4) {
        if(xhr.status >= '200' && xhr.status <= 300){
          resolve(xhr.responeText)
        } else {
          reject(xhr)
        }
      }
    })
    // Call the send() method to pass the parameters
    xhr.send(body)
  })
}
Copy the code

Async/await

function asyncToGenerator(generatorFunc) {
  // Returns a new function
  return function() {
  
    // Call generator to generate iterators
    Var gen = testG()
    const gen = generatorFunc.apply(this.arguments)

    // Return a promise because the outside is using the return value of this function either as.then or await
    // var test = asyncToGenerator(testG)
    // test().then(res => console.log(res))
    return new Promise((resolve, reject) = > {
    
      // Internally define a step function to override the yield barrier step by step
      // Key has two values, next and throw, corresponding to gen's next and throw methods respectively
      The arg argument is used to yield the promise resolve value to the next yield
      function step(key, arg) {
        let generatorResult
        
        // This method needs to be wrapped in a try catch
        // If an error occurs, discard the promise, reject the error, and catch the error
        try {
          generatorResult = gen[key](arg)
        } catch (error) {
          return reject(error)
        }

        // gen.next() results in a {value, done} structure
        const { value, done } = generatorResult

        if (done) {
          // If this is already done, resolve the promise
          // This done will not be true until the last call to next
          {done: true, value: 'success'}
          // This value is the return value of the generator function
          return resolve(value)
        } else {
          // Call gen.next() every time except at the end
          {value: Promise, done: false}
          Resolve accepts a Promise as an argument
          // Then will only be called when the promise argument is resolved
          return Promise.resolve(
            // This value corresponds to the promise after yield
            value
          ).then(
            // When the value promise is resove, next is executed
            // And whenever done is not true, the promise is recursively unwrapped
            // Next ().value.then(value => {
            // gen.next(value).value.then(value2 => {
            // gen.next()
            //
            // // now done is true and the entire promise is resolved
            // // the most external test().then(res => console.log(res)) then starts execution
            / /})
            // })
            function onResolve(val) {
              step("next", val);
            },
            // If promise is rejected, enter step again
            // The difference is that this try catch calls Gen. throw(err).
            // Then you catch the promise, reject it
            function onReject(err) {
              step("throw", err);
            },
          )
        }
      }
      step("next"); }}})Copy the code

8. Array flattening

1. Ordinary recursion

var arr = [1.2.3.4[4.5[5.6.7]]]
function flatten(arr) {
  let array = [];
  for (let i=0; i<arr.length; i++) {
    if (Array.isArray(arr[i])) {
      array = array.concat(flatten(arr[i]));
    } else{ array.push(arr[i]); }}return array;
}
const result = flatten(arr);
console.log(result);
Copy the code

2. reduce

var arr = [1.2.3.4[4.5[5.6.7]]];
function flatten(arr) {
  return arr.reduce((prev, cur) = > {
    return prev.concat(Array.isArray(cur) ? flatten(cur) : cur); }}, [])const result = flatten(arr);
console.log(result);
Copy the code

3.toString + split

var arr = [1.2.3.4[4.5[5.6.7]]];
function flatten(arr) {
  return arr.toString().split(', ').map((res) = >{
    return Number(res); })}const result = flatten(arr);
console.log(result);
Copy the code

4. Invoke Flat in ES6

var arr = [1.2.3.4[4.5[5.6.7]]];
function flatten(arr) {
    return arr.flat(Infinity);
}
const result = flatten(arr);
console.log(result);
Copy the code

5. Regex and JSON methods are processed together

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

6. Flatten with the second argument — remember

// Array flattening
const arr = [1.2.3.4[4[5.6[7.8.9]]]];
function flatten(arr, index) {
  return index > 0 ? arr.reduce((prev, curr) = > {
    return prev.concat(Array.isArray(curr) ? flatten(curr, index - 1) : curr);
  }, []) : arr.slice();
}
console.log(arr.flat(2));
console.log(flatten(arr, 2));
Copy the code

Deformation of 7.

// Array flattening
const arr = [1.2.3.4[4[5.6[7.8.9]]]];
function flatten(arr, index) {
  let array = [];
  if (index > 0) {
    for (let i=0; i<arr.length; i++) {
      if (Array.isArray(arr[i])) {
        array = array.concat(flatten(arr[i], index - 1))}else {
        array.push(arr[i])
      }
    }
  } else {
    array = arr.slice();
  }
  return array;
}
console.log(arr.flat(3));
console.log(flatten(arr, 3));
Copy the code

8. 2 – forEach deformation

// Array flattening
const arr = [1.2.3.4[4[5.6[7.8.9]]]];
function flatten(arr, index) {
  let array = [];
  if (index > 0) {
    arr.forEach(item= > {
      if (Array.isArray(item)) {
        array = array.concat(flatten(item, index - 1))}else{ array.push(item); }})}else {
    array = arr.slice();
  }
  return array;
}
console.log(arr.flat(2));
console.log(flatten(arr, 2));
Copy the code

9. Instanceof function

  • instanceofIs to judgeAWhether it isB, the expression is:A instanceof BIf theABIs returnedtrueOtherwise returnfalse;
  • instanceofThe operator tests whether an object has a constructor in its stereotype chainprototypeProperties;
  • Cannot detect basic data types, results on the prototype chain may not be accurate, cannot detectnull,undefined;
  • Implementation: Iterate through the prototype chain for the left variable until it finds the one for the right variableprototypeIf not found, returnfalse;
function instanceOf(left, right) {
  // Get the prototype of the object
  var proto = left.__proto__;
  // Get the prototype of the type
  var prototype = right.prototype;
  while(true) {
    if (proto === null) return false;
    if (proto === prototype) return true; proto = proto.__proto__; }}function a() {
  this.name = 'dell';
  this.age = 18;
}
var newA = new a();
console.log(instanceOf(newA, a))
Copy the code

10. Various sorts of arrays

1. Bubble sort

var a = [1.3.6.3.23.76.1.34.222.6.456.221];
function bubbleSort(array) {
  if (array.length < 2) return array;
  for (let i=0; i<array.length; i++) {
    for (let j=i+1; j<array.length; j++) {
      if (array[j]<array[i]) {
        letnum = array[i]; array[i] = array[j]; array[j] = num; }}}return array;
}
let arr = bubbleSort(a);
console.log(arr);
Copy the code

2. Quicksort

var a = [1.3.6.3.23.76.1.34.222.6.456.221];
function quickSort(array) {
  if (array.length <= 1) return array;
  const len = array.length;
  const index = Math.floor(len >> 1);
  const pivot = array.splice(index, 1) [0]; // Using splice returns the deleted form array
  const left = [];
  const right = [];
  for (let i=0; i<len; i++) {
    if (array[i] <= pivot) {
      left.push(array[i]);
    } else if (array[i] > pivot) { If {} else {} else {}right.push(array[i]); }}return quickSort(left).concat([pivot], quickSort(right));
}
const arr = quickSort(a);
console.log(arr);
Copy the code

3. Insert sort

var a = [1.3.6.3.23.76.1.34.222.6.456.221];
function insertSort(array) {
  const len = array.length;
  let current;
  let prev;
  for (let i=1; i<len; i++) {
    current = array[i];
    prev = i - 1;
    while (prev >= 0 && array[prev] > current) {
      array[prev + 1] = array[prev];
      prev--;
    }
    array[prev + 1] = current;
  }
  return array
}
const arr = insertSort(a);
console.log(arr);
Copy the code

4. Select sort

var a = [1, 3, 6, 3, 23, 76, 1, 34, 222, 6, 456, 221];
function selectSort(array) {
  const len = array.length
  let temp
  let minIndex
  for (let i = 0; i < len - 1; i++) {
    minIndex = i;
    for (let j = i + 1; j < len; j++) {
      if (array[j] <= array[minIndex]) {
        minIndex = j;
      }
    }
    temp = array[i];
    array[i] = array[minIndex];
    array[minIndex] = temp;
  }
  return array;
}
selectSort(a); // [1, 1, 3, 3, 6, 6, 23, 34, 76, 221, 222, 456]
Copy the code

5. Heap sort

var a = [1.3.6.3.23.76.1.34.222.6.456.221];
function heap_sort(arr) {
  var len = arr.length;
  var k = 0;
  function swap(i, j) {
    var temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
  }
  function max_heapify(start, end) {
    var dad = start;
    var son = dad * 2 + 1;
    if (son >= end) return;
    if (son + 1 < end && arr[son] < arr[son + 1]) {
      son++;
    }
    if(arr[dad] <= arr[son]) { swap(dad, son); max_heapify(son, end); }}for (var i = Math.floor(len / 2) - 1; i >= 0; i--) {
    max_heapify(i, len)
  }  
  for (var j = len - 1; j > k; j--) {
    swap(0, j);
    max_heapify(0, j);
  }  
  return arr;
}
heap_sort(a); // [1, 1, 3, 3, 6, 6, 23, 34, 76, 221, 222, 456]
Copy the code

6. Merge sort

var a = [1.3.6.3.23.76.1.34.222.6.456.221];
function mergeSort(array) {
  const merge = (right, left) = > {
    const result = [];
    let il = 0;
    let ir = 0;
    while (il < left.length && ir < right.length) {
      if (left[il] < right[ir]) {
        result.push(left[il++]);
      } else{ result.push(right[ir++]); }}while (il < left.length) {
      result.push(left[il++]);
    }
    while (ir < right.length) {
      result.push(right[ir++]);
    }
    return result;
  }
  const mergeSort = array= > {
    if (array.length === 1) { return array }
    const mid = Math.floor(array.length / 2);
    const left = array.slice(0, mid);
    const right = array.slice(mid, array.length);
    return merge(mergeSort(left), mergeSort(right));
  }
  return mergeSort(array);
}
mergeSort(a); // [1, 1, 3, 3, 6, 6, 23, 34, 76, 221, 222, 456]
Copy the code

11. Currization functions

// recursive traversal
function curry(fn, ... args) {
  var len = fn.length;
  var args = args || [];
  return function() {
    var newArgs = args.concat(Array.prototype.slice.call(arguments));
    if (newArgs.length < len) {
      return curry.call(this, fn, ... newArgs) }else {
      return fn.apply(this, newArgs); }}}function add(a, b, c) {
  return a + b + c;
}
var a = curry(add);
console.log(a(1.2.3));
Copy the code
// ES6
function curry(fn, ... args) {
  if (args.length < fn.length) return (. args1) = >curry(fn, ... args, ... args1);else returnfn(... args); }function add(a, b, c) {
  return a + b + c;
}
var a = curry(add);
var b = a(1.2.3);
console.log(b);
Copy the code

Closure JS Basic Programming questions (bytes)

var foo = function(. args) {
 // Implement the function body
}
var f1 = foo(1.2.3);
f1.getValue(); // 6 Output is the sum of parameters
var f2 = foo(1) (2.3);
f2.getValue(); / / 6
var f3 = foo(1) (2) (3) (4);
f3.getValue(); / / 10
Copy the code

answer

var foo = function(. args) {
  const target = (. args2) = >foo(... args, ... args2); target.getValue =() = > args.reduce((p, q) = > p + q, 0)
  return target
}
var f1 = foo(1.2.3);
console.log(f1.getValue()) // 6 Output is the sum of parameters
var f2 = foo(1) (2.3);
console.log(f2.getValue()); / / 6
var f3 = foo(1) (2) (3) (4);
console.log(f3.getValue()) / / 10
Copy the code

Currie transformation programming problem

Complete the combo function. It takes any number of single-parameter functions (functions that take only one argument) as arguments and returns a function. Function calls such as f(g(h(a)) can be abbreviated as combo(f, g, h)(a).

// ES6 -- reduce
const combo = (. args) = > {
  args.length && args.reverse()
  return prop= >
    args.reduce((acc, cur) = > {
      return cur(acc);
    }, prop)
}
// ES5 -- for
function combo () {
  const cbs = [...arguments].reverse();
  return function () {
    var res = cbs[0] (... arguments);for (let i = 1; i < cbs.length; i++) {
      res = cbs[i](res);
    }
    returnres; }}/* Here is the test code */
const addOne = (a) = > a + 1
const multiTwo = (a) = > a * 2
const divThree = (a) = > a / 3
const toString = (a) = > a + ' '
const split = (a) = > a.split(' ')

split(toString(addOne(multiTwo(divThree(Awesome!)))))
// => ["4", "4", "5"]

const testForCombo = combo(split, toString, addOne, multiTwo, divThree)
testForCombo(Awesome!)
// => ["4", "4", "5"]
Copy the code

12, array deduplication

1. Set

let arr = [1.2.3.4.4.5.6.7];
[...new Set(arr)]
Copy the code

2. Set + Array.from

let arr = [1.2.3.4.4.5.6.7];
function unique(arr) {
  return Array.from(new Set(arr));
}
Copy the code

3. reduce + includes

let arr = [1.2.3.4.4.5.6.7];
function unique(arr) {
  return arr.reduce((prev, cur) = > prev.includes(cur) ? prev : [...prev, cur], []);
}
const uniqueArr = unique(arr);
console.log(uniqueArr);
Copy the code

4. filter + indexOf

let arr = [1.2.3.4.4.5.6.7];
function unique(arr) {
  return arr.filter((item, index, arr) = > {
    returnarr.indexOf(item) === index; })}const uniqueArr = unique(arr);
console.log(uniqueArr);
Copy the code

5. for + indexOf

let arr = [1.2.3.4.4.5.6.7];
function unique(arr) {
  let array = []
  for (let i=0; i<arr.length; i++) {
    if(arr.indexOf(arr[i]) === i) { array.push(arr[i]); }}return array;
}
const uniqueArr = unique(arr);
console.log(uniqueArr)
Copy the code

6. for + indexOf

let arr = [1.2.3.4.4.5.6.7];
function unique(arr) {
  let array = [];
  for (let i=0; i<arr.length; i++) {
    if (array.indexOf(arr[i]) === -1) { array.push(arr[i]); }}return array;
}
const uniqueArr = unique(arr);
console.log(uniqueArr);
Copy the code

7. for + includes

let arr = [1.2.3.4.4.5.6.7];
function unique(arr) {
  let array = [];
  for (let i=0; i<arr.length; i++) {
    if(! array.includes(arr[i])) { array.push(arr[i]) } }return array
}
const uniqueArr = unique(arr);
console.log(uniqueArr);
Copy the code

Publish and subscribe

// We write it as ES6 class
class EventEmitter {
  constructor() {
    // The event object that stores the subscription type
    this.events = Object.create(null);
  }
  /** * Register event listener *@param {String} Type Indicates the event type *@param {Function} Cb callback function */
  on(type, cb) {
    // If the type is present, continue adding the callback cb to the array
    if (this.events[type]) {
      this.events[type].push(cb);
    } else {
      // The first time type is stored, an array space is created and stored to callback cb
      this.events[type] = [cb]; }}/** * Release event *@param {String} Type Indicates the event type *@param  {... any} Args argument list, which assigns the arguments passed by emit to the callback function */
  emit(type, ... args) {
    // Run through the array of type subscriptions
    if (this.events[type]) {
      this.events[type].forEach(listener= > {
        listener.call(this. args); }); }}/** * Removes one (same) listener * for an event@param {String} Type Indicates the event type *@param {Function} Cb callback function */
  off(type, cb) {
    if (this.events[type]) {
      this.events[type] = this.events[type].filter(listener= > {
        // Filter the unused callback cb
        returnlistener ! == cb && listener.listen ! == cb; }); }if (this.events[type].length === 0) {
      delete this.events[type]; }}/** * Removes all listeners * for an event@param {String} Type Indicates the event type */
  offAll(type) {
    if (this.events[type]) {
      delete this.events[type]; }}/** * Only triggers publication once *@param {String} Type Indicates the event type *@param {Function} Cb callback function */
  once(type, cb) {
    function wrap() { cb(... arguments);this.off(type, wrap);
    }
    // Bind first, call then delete
    wrap.listen = cb;
    Call the on method directly
    this.on(type, wrap); }}/ / test
// Create an event manager instance
const ee = new EventEmitter();
// Register a chifan event listener
ee.on('chifan'.function() { console.log('Dinner, let's go! ')})// Publish the event chifan
ee.emit('chifan');
// Emit can also pass arguments
ee.on('chifan'.function(address, food) { console.log('Dinner, let's go${address}eat${food}! `)}); ee.emit('chifan'.'Three Canteen'.'Iron plate rice'); // Two messages are printed because two listeners for chifan events were registered

// Test removing event listening
const toBeRemovedListener = function() { console.log('I'm a listener that can be removed.')}; ee.on('testoff', toBeRemovedListener);
ee.emit('testoff');
ee.off('testoff', toBeRemovedListener);
ee.emit('testoff'); // Now the event listener has been removed and no more console.log will be printed

// Test to remove all event listening for chifan
ee.offAll('chifan');
console.log(ee); // Notice that the ee.listeners have become empty objects, and the listeners will not respond to sending chifan events
Copy the code

14, depth copy

1. The shallow copy

var obj = {a: 1.b: 2.c: { d: 1.e: 5 }}
function shallowClone(target) {
  if (typeof target === 'object'&& target ! = =null) {
    var obj = Array.isArray(target) ? [] : {};
    for (var key in target) {
      if (target.hasOwnProperty(key)) {
        obj[key] = target[key]
      }
    }
    return obj;
  }

// The first deep copy is followed by the shallow copy
var o = shallowClone(obj);
obj.c.d = 2;
console.log(obj); // {a: 1, b: 2, c: {d:2, e:5}}
console.log(o); // {a: 1, b: 2, c: {d:2, e:5}}
Copy the code

2. Deep copy

  • Determine the type, re, and date to return the new object directly
  • An empty or non-object type that returns the original value
  • Consider circular references and determine ifhashContains a direct returnhashThe values in the
  • Create a new correspondingnew obj.constructorjoinhash
  • Traversal object recursion (commonkeykeysymbolSituation)
/ / basic version
var obj = {a: 1.b: 2.c: { d: 1.e: 5 }}
function deepClone(target) {
  if (typeoftarget ! = ='object'&& target ! = =null) {
    throw new Error('Please enter an object/array');
  }
  let obj = Array.isArray(target) ? [] : {};
  for (const key in target) {
    if (target.hasOwnProperty(key)) {
      if (typeof target[key] === 'object') {
        obj[key] = deepClone(target[key]);
      } else{ obj[key] = target[key]; }}}return obj;
}
var o = deepClone(obj);
obj.c.d = 2;
console.log(obj); // {a: 1, b: 2, c: {d: 2, e: 5}}
console.log(o); // {a: 1, b: 2, c: {d: 1, e: 5}}
Copy the code
/ / premium
function deepClone(obj,hash = new WeakMap(a)) {
  if(obj instanceof RegExp) return new RegExp(obj);
  if(obj instanceof Date) return new Date(obj);
  if(obj === null || typeofobj ! = ='object') return obj;
  // Loop references;
  if(hash.has(obj)) {
    return hash.get(obj)
  }
  // new a corresponding object;
  // obj = Array, equivalent to new Array();
  // obj = Object, equivalent to new Object();
  let constr = new obj.constructor();
  hash.set(obj,constr);
  for(let key in obj) {
    if(obj.hasOwnProperty(key)) {
      constr[key] = deepClone(obj[key],hash)
    }
  }
  // consider the symbol case;
  let symbolObj = Object.getOwnPropertySymbols(obj);
  for(let i=0; i<symbolObj.length; i++){if(obj.hasOwnProperty(symbolObj[i])) {
      constr[symbolObj[i]] = deepClone(obj[symbolObj[i]],hash)
    }
  }
  return constr
}
Copy the code

Implement the sleep method

1. The callback

function sleep(callback, time) {
  setTimeout(callback, time);
}
function sayHi() {
  console.log('satHi');
}
sleep(sayHi, 1000);
Copy the code

2. promise

/ / a simple version
function sleep(time) {
  return new Promise((resolve, reject) = > setTimeout(resolve, time));
}
sleep(1000).then(() = > { console.log('sayHi')});Copy the code
/ / premium
function sleep(fn, time, ... args) {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      // reslove(fn(... args))
      Promise.resolve(fn(... args)).then(resolve).catch(reject); }, time) }) }Copy the code

3. Generator

function* sleep(time) {
  yield new Promise(function(resolve, reject) {
    setTimeout(resolve, time);
  })
}
sleep(1000).next().value.then(() = >{console.log('sayHi')});
Copy the code

4. async / await

function sleep(time) {
  return new Promise((resolve, reject) = > {
    setTimeout(resolve, time);
  })
}
aysnc function output(time) {
  const result = await sleep(time);
  console.log('sayHi');
  return result;
}
output(1000)
Copy the code

Implement object.create ()

  • MDNDocument;
  • Object.create()Takes the parameter object as a prototype of a newly created empty object and returns it;
/ / a simple version
function myCreate(obj) {
  // declare a new function
  function C() {};
  // Point the function's prototype to obj
  C.prototype = obj;
  // Return the power object of this function
  return new C();
}
// Official version of Polyfill
if (typeof Object.create ! = ="function") {
  Object.create = function (proto, propertiesObject) {
    if (typeofproto ! = ='object' && typeofproto ! = ='function') {
      throw new TypeError('Object prototype may only be an Object: ' + proto);
    } else if (proto === null) {
      throw new Error("This browser's implementation of Object.create is a shim and doesn't support 'null' as the first argument.");
    }

    if (typeofpropertiesObject ! = ='undefined') throw new Error("This browser's implementation of Object.create is a shim and doesn't support a second argument.");

    function F() {}
    F.prototype = proto;

    return new F();
  };
}
Copy the code

17, implement Object.assign

Object.assign2 = function(target, ... source) {
  if (target === null || target === undefined) {
    throw new TypeError('Cannot convert undefined or null to object')}let ret = Object(target);
  source.forEach(function(obj) {
    if(obj ! = =null|| obj ! = =undefined) {
      for (let key in obj) {
        if(obj.hasOwnProperty(key)) { ret[key] = obj[key]; }}}})return ret;
}
Copy the code

18. Print every second (closure)

for (var i=1; i<=5; i++) {
  (function (i) {
    setTimeout(() = > console.log(i), 1000*i);
  })(i)
}
for (var i=1; i<=5; i++) {
  setTimeout(function(i) {
    console.log(i);
  }.bind(null, i), 1000*i)
}
Copy the code

Write a JSONP by hand

let jsonp = function (url, data = {}, callback='callback') {
  // Convert data to a URL string
  let dataStr = url.indexOf('? ') = = = -1 ? '? ' : '&'
  for(let key in data) {
    dataStr += `${key}=${data[key]}& `;
  }
  // Handle callback functions in the URL
  dataStr += 'callback=' + callback;
  // Create the srcipt tag and add the SRC attribute value
  let scriptBody = document.createElement('script')
  scriptBody.src = url + dataStr

  // Append to the page to initiate the request immediately
  document.body.appendChild(scriptBody);
  // Return a promise
  return new Promise((resolve, reject) = > {
    window[callback] = (data) = > {
      try {
        resolve(data)
      } catch(e) {
        reject(e)
      } finally {
        // Remove the script element
        scriptBody.parentNode.removeChild(scriptBody);
        console.log(scriptBody);
      }
    }
  })
}

jsonp('https://photo.sina.cn/aj/index', {page:1.cate:'recommend'}).then(res= >{console.log(res)})
Copy the code

20. Handwritten observer mode

In the Observer mode, there are only two subjects, the target object Subject and the Observer Observer.

  • Observers need to beObserverIn order to achieveupdateMethod to be called by the target object.updateMethod to execute custom business code.
  • The target objectSubjectAlso known as the observed or the subject, it has a very simple function, it can be understood as managing only one kind of event.SubjectYou need to maintain your own array of observersobserverListWhen itself changes, by calling itsnotifyMethod, telling each observer in turn to executeupdateMethods.
/ / observer
class Observer {
  /** * constructor *@param {Function} Cb callback function that executes */ when receiving a notification from the target object
  constructor(cb){
    if (typeof cb === 'function') {
      this.cb = cb;
    } else {
      throw new Error('Observer constructor must pass function type! '); }}/** * execute */ when notified by the target object
  update() {
    this.cb(); }}// Target object
class Subject {
  constructor() {
    // Maintain a list of observers
    this.observerList = [];
  }
  /** * Add an observer *@param {Observer} Observer Observer instance */
  addObserver(observer) {
    this.observerList.push(observer);
  }
  /** * notify all observers */
  notify() {
    this.observerList.forEach(observer= >{ observer.update(); }}})const observerCallback = function() {
  console.log('I've been informed.');
}
const observer = new Observer(observerCallback);

const subject = new Subject();
subject.addObserver(observer);
subject.notify();
Copy the code

21. Get the object content

Complete the deepGet function and pass it an object and a string. The string represents the path to the object’s deep properties.

const deepGet = (obj, prop) = > {
  const keyArr = prop.split('. ').map(key= > key);

  const reducer = (acc, cur) = > {
    const objKey = cur.includes('[') && cur.replaceAll(/[\[?0-9\]$]/gi.' ');
    if (Array.isArray(acc[objKey])) {
      cur = cur.replaceAll(/[^?0-9]/gi.' ');
      return acc[objKey][cur] || {};
    }
    return acc[cur] ? acc[cur] : {};
  }

  const result = keyArr.reduce(reducer, obj);
  return Object.keys(result).length ? result : undefined;
}

/** Here is the test code */
deepGet({
  school: {
    student: { name: 'Tomy'}},},'school.student.name') // => 'Tomy'

deepGet({
  school: {
    students: [{name: 'Tomy' },
      { name: 'Lucy'},]}},'school.students[1].name') // => 'Lucy'

// For nonexistent attributes, return undefined
deepGet({ user: { name: 'Tomy'}},'user.age'); // => undefined
deepGet({ user: { name: 'Tomy'}},'school.user.age'); // => undefined
Copy the code

22, various methods to generate random numbers?

// Generate a random number for [min, Max], where math.random () is a random number for [0, 1)
function getRandom(min, max) {
  return Math.floor(Math.random() * (max - min)) + min   
}
// Generate a random number for [min, Max]
function getRandom(min, max) {
  return Math.floor(Math.random() * (max + 1 - min)) + min
}
// Generate (min, Max) random number
function getRandom(min, max) {
  let result = Math.random()*(m-n)+n;
  while(result == n) {
    result = Math.random()*(m-n)+n;
  }
  return result;
}
// Generate (min, Max) random number
function getRandom(min, max) {
  let result = Math.random()*(m-n+1)+n-1;
  while(result<n) {
    result = Math.random()*(m-n+1)+n-1;
  }
  return result;
}
Copy the code

23, How to implement random array sort?

let arr = [2.3.454.34.324.32];
arr.sort(randomSort);
function randomSort(a, b) {
  return Math.random() > 0.5 ? -1 : 1;
}
Copy the code

24, to achieve regular segmentation thousandths

// No decimal point
let num1 = '1321434322222'
num1.replace(/(\d)(? =(\d{3})+$)/g.'$1');
// Has a decimal point
let num2 = '342243242322.3432423';
num2.replace(/(\d)(? =(\d{3})+\.) /g.'$1');
Copy the code

Parse the URL Params as an object

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

      if (paramsObj.hasOwnProperty(key)) { // Add a value if the object has a 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;
}
Copy the code

26. Template engine implementation

let template = 'I am {{name}}, age {{age}}, gender {{sex}}';
let data = {
  name: 'name'.age: 18
}
render(template, data); // I am name, age 18, gender undefined
Copy the code
/ / method
function render(template, data) {
  const reg = /\{\{(\w+)\}\}/; // The template string is regular
  if (reg.test(template)) { // Check if there is a template string in the template
    const name = reg.exec(template)[1]; // Find the first template string field in the current template
    template = template.replace(reg, data[name]); // Render the first template string
    return render(template, data); // Recursively render and return the rendered structure
  }
  return template; // If the template does not have a template string
}
Copy the code
// Method 2 (recommended)
function render(template, data) {
  let computed = template.replace(/\{\{(\w+)\}\}/g.function (match, key) {
    return data[key];
  });
  return computed;
}
Copy the code

27. Convert to hump naming

var s1 = "get-element-by-id";
// Convert to getElementById
Copy the code
var f = function(s) {
  return s.replace(/-\w/g.function(x) {
    return x.slice(1).toUpperCase(); })}Copy the code

Find the maximum number of characters in a string

  • Example: the most common abbcccDDDDd -> character is D, which occurs 5 times
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 = $0.length;
    char = $1; }});console.log('the most characters are${char}Appeared,${num}Time `);
Copy the code

Lazy loading of images

let imgList = [...document.querySelectorAll('img')]
let length = imgList.length

const imgLazyLoad = function() {
  let count = 0
  return (function() {
    let deleteIndexList = []
    imgList.forEach((img, index) = > {
      let rect = img.getBoundingClientRect()
      if (rect.top < window.innerHeight) {
        img.src = img.dataset.src;
        deleteIndexList.push(index);
        count++;
        if (count === length) {
          document.removeEventListener('scroll', imgLazyLoad);
        }
      }
    })
    imgList = imgList.filter((img, index) = >! deleteIndexList.includes(index)); }}) ()// It is better to add anti-shake treatment here
document.addEventListener('scroll', imgLazyLoad);
Copy the code

30. Implement Reduce and forEach

// reduce
Array.prototype.reduce = function(fn, val) {
  // Check if val has a value
  for (let i = 0; i < this.length; i++) {
    // No value is passed in
    if (typeof val === 'undefined') {
      val = this[i];
    } else { // Val is passed in
      // total is the initial value val
      val = fn(val, this[i], i, this); }}return val;
};
Copy the code
// forEach
// Base version
Array.prototype.forEach = function(callback) {
  var len = this.length;
  for(var i = 0; i < len; i++) {
    callback(this[i], i, this); }}/ / call
Array.prototype.forEach = function(callback, thisArg) {
  var len = this.length;
  for(var i = 0; i < len; i++) {
   // callback(this[i], i, this);
   callback.call(thisArg, this[i], i, this); }}/ / the bind
Array.prototype.forEach = function(callback, thisArg) {
  var len = this.length;
  callback = callback.bind(thisArg);
  for(var i = 0; i < len; i++) {
    callback(this[i], i, this); }}Copy the code

Use setTimeout to implement setInterval

let timer = null;
function mockSetInterval(fn, delay, ... args) {
  let recur = function() {
    timer = setTimeout(() = > {
      fn.apply(this, args);
      recur();
    }, delay)
  }
  recur();
}

function mockClearInterval(id) {
  clearTimeout(id);
}

mockSetInterval((a, b) = > {console.log(a, b)}, 1000.'1'.'2')

setTimeout(() = > {
  mockClearInterval(timer);
}, 4000)
Copy the code

Implement the chunk function

  • Divide the array into segments based on size, for example, arr = [1,2,3,4,5,6,7], size = 2;
  • Target = [[1,2], [3,4], [5,6], [7]
function chunk(arr, size) {
  let target = [];
  let count = size;
  if (arr.length < size) {
    target.push(arr.slice());
  } else {
    let len = arr.length - arr.length % size;
    for (let i=0; i<len; i=i+size) {
      let newArr = arr.slice(i, count);
      count = count + size;
      target.push(newArr);
    }
    if(arr.length % size ! = =0) { target.push(arr.slice(arr.length - arr.length % size)); }}return target;
}

let target = chunk([1.2.3.4.5.6].7);
console.log(target);
Copy the code

Achieve traffic lights

  • Using a div for the traffic light effect, you rotate a circular div with green 3s, yellow 1s, and red 2s
#traffic-color {
  width: 100px;
  height: 100px;
  overflow: hidden;
  border-radius: 50%;
}
Copy the code
<div id="traffic-color"></div>

const TRAFFIC_COLOR_CONFIG = {
  'green': 3000.'yellow': 1000.'red': 2000
}
function delay(duration) {
  return new Promise((resolve, reject) = > setTimeout(resolve, duration));
}

async function changeColor(color) {
  document.getElementById('traffic-color').style.background = color;
  await delay(TRAFFIC_COLOR_CONFIG[color]);
}

async function run() {
  // while(1) {
  // await changeColor('green', 3000);
  // await changeColor('yellow', 1000);
  // await changeColor('red', 2000);
  // }
  // await changeColor('green', 3000);
  // await changeColor('yellow', 1000);
  // await changeColor('red', 2000);
  
  for (const key in TRAFFIC_COLOR_CONFIG) {
    await changeColor(key);
  }
  run()
}
run();
Copy the code

34, Implement indexOf()

A few points to note:

  • Returns -1 if the index starts larger than the string/array length;
  • If the index starts less than or equal to 0, the index starts from 0.
  • If the string or array length is 0, return 0;
  • Otherwise, return -1;
function indexOf(searchValue, index=0) {
  if (this.length < 1 || index > this.length) return -1;
  if(! searchValue)return 0;
  index = index <= 0 ? 0 : index;
  for (let i=0; i<this.length; i++) {
    if (this[i] === searchValue) {
      returni; }}return -1;
}
Array.prototype.myIndexOf = indexOf;
String.prototype.myIndexOf = indexOf;
console.log([1.2.3].myIndexOf(2));
console.log('123'.myIndexOf('2'));
Copy the code

35, write version number sort method

  • Title description: a group of version number as follows [‘ while ‘, ‘2.3.3’, ‘0.302.1’, ‘4.2’, ‘4.3.5’, ‘4.3.4.5’]. Now you need to sort it;
  • Sorting result for [‘ 4.3.5 ‘, ‘4.3.4.5’, ‘2.3.3’, ‘0.302.1’, ‘while’].
arr.sort((a, b) = > {
  let i = 0;
  const arr1 = a.split(".");
  const arr2 = b.split(".");

  while (true) {
    const s1 = arr1[i];
    const s2 = arr2[i];
    i++;
    if (s1 === undefined || s2 === undefined) {
      return arr2.length - arr1.length;
    }

    if (s1 === s2) continue;

    returns2 - s1; }});console.log(arr);
Copy the code

Class array to array method

  • How do I convert an array of classes to an array?
const arrayLike=document.querySelectorAll('div')

// 1. Extend the operator
[...arrayLike]
// 2.Array.from
Array.from(arrayLike)
// 3.Array.prototype.slice
Array.prototype.slice.call(arrayLike)
// 4.Array.apply
Array.apply(null, arrayLike)
// 5.Array.prototype.concat
Array.prototype.concat.apply([], arrayLike)
Copy the code

Object.is()

  • Object.is does not convert the type of the two values being compared, much like ===, although there are some differences.
      1. NaN is not equal in ===, but is equal in object.is ();
      1. +0 and -0 are equal in ===, but not in object.is ();
Object.is = function (x, y) {
  if (x === y) {
    // In the current case, only one case is special: + 0-0
    // If x! == 0, returns true
    Infinity Infinity Infinity Infinity Infinity Infinity Infinity Infinity Infinity Infinity Infinity Infinity Infinity Infinity Infinity Infinity
    returnx ! = =0 || 1 / x === 1 / y;
  }

  // x ! In the case of == y, we just need to determine if it is NaN, if x! If ==x, then x is NaN, and likewise y
  // Return true if x and y are both NaN
  returnx ! == x && y ! == y; };Copy the code

38. Virtual DOM is converted into real DOM

  • JSONFormat virtualisationDOMHow do you make it realDOM;

Title description:

{
  tag: 'DIV'.attrs: {id:'app'
  },
  children: [{tag: 'SPAN'.children: [{tag: 'A'.children: []}]}, {tag: 'SPAN'.children: [{tag: 'A'.children: []}, {tag: 'A'.children: []}]}} virtualize the appeal`DOM`Into the truth below`DOM`;
<div id="app">
  <span>
    <a></a>
  </span>
  <span>
    <a></a>
    <a></a>
  </span>
</div>
Copy the code

The implementation code is as follows:

// Real render function
function _render(vnode) {
  // If it is a numeric type, convert it to a string
  if (typeof vnode === "number") {
    vnode = String(vnode);
  }
  // The string type is directly a text node
  if (typeof vnode === "string") {
    return document.createTextNode(vnode);
  }
  / / common DOM
  const dom = document.createElement(vnode.tag);
  if (vnode.attrs) {
    // Iterate over attributes
    Object.keys(vnode.attrs).forEach((key) = > {
      const value = vnode.attrs[key];
      dom.setAttribute(key, value);
    });
  }
  // Subarrays operate recursively
  vnode.children.forEach((child) = > dom.appendChild(_render(child)));
  return dom;
}
Copy the code

39. DOM node output JSON format

Title description:

<div>
  <span>
    <a></a>
  </span>
  <span>
    <a></a>
    <a></a>
  </span>< / div > the appeal`DOM`The structure turns into the one below`JSON`Format {tag: 'DIV'.children: [{tag: 'SPAN'.children: [{tag: 'A'.children: []}]}, {tag: 'SPAN'.children: [{tag: 'A'.children: []}, {tag: 'A'.children: []}]}Copy the code

The implementation code is as follows:

function dom2Json(domtree) {
  let obj = {};
  obj.name = domtree.tagName;
  obj.children = [];
  domtree.childNodes.forEach((child) = > obj.children.push(dom2Json(child)));
  return obj;
}
Copy the code

Implement the Flatten method of an object

Title description:

const obj = {
  a: {
    b: 1.c: 2.d: {e: 5}},b: [1.3, {a: 2.b: 3}].c: 3} flatten(obj) returns the following result/ / {
// 'a.b': 1,
// 'a.c': 2,
// 'a.d.e': 5,
// 'b[0]': 1,
// 'b[1]': 3,
// 'b[2].a': 2,
// 'b[2].b': 3
// c: 3
// }
Copy the code

The implementation code is as follows:

function isObject(val) {
  return typeof val === "object"&& val ! = =null;
}

function flatten(obj) {
  if(! isObject(obj)) {return;
  }
  let res = {};
  const dfs = (cur, prefix) = > {
    if (isObject(cur)) {
      if (Array.isArray(cur)) {
        cur.forEach((item, index) = > {
          dfs(item, `${prefix}[${index}] `);
        });
      } else {
        for (let k in cur) {
          dfs(cur[k], `${prefix}${prefix ? "." : ""}${k}`); }}}else{ res[prefix] = cur; }}; dfs(obj,"");

  return res;
}
flatten();
Copy the code

41. Lists become trees

Title description:

[{id: 1.text: Nodes' 1 '.parentId: 0 // The top-level node is represented by 0
  },
  {
    id: 2.text: '1 _1 nodes'.parentId: 1 // Use this field to determine the child and parent levels
  }
  ...
]

转成
[
  {
    id: 1.text: Nodes' 1 '.parentId: 0.children: [{id: 2.text: '1 _1 nodes'.parentId: 1}}]]Copy the code

The implementation code is as follows:

function listToTree(data) {
  let temp = {};
  let treeData = [];
  for (let i = 0; i < data.length; i++) {
    temp[data[i].id] = data[i];
  }
  for (let i in temp) {
    if(+temp[i].parentId ! =0) {
      if(! temp[temp[i].parentId].children) { temp[temp[i].parentId].children = []; } temp[temp[i].parentId].children.push(temp[i]); }else{ treeData.push(temp[i]); }}return treeData;
}
Copy the code

42. Tree structure to list

Title description:

[{id: 1.text: Nodes' 1 '.parentId: 0.children: [{id:2.text: '1 _1 nodes'.parentId:1}]}] into [{id: 1.text: Nodes' 1 '.parentId: 0 // The top-level node is represented by 0
  },
  {
    id: 2.text: '1 _1 nodes'.parentId: 1 // Use this field to determine the child and parent levels}... ]Copy the code

The implementation code is as follows:

function treeToList(data) {
  let res = [];
  const dfs = (tree) = > {
    tree.forEach((item) = > {
      if (item.children) {
        dfs(item.children);
        delete item.children;
      }
      res.push(item);
    });
  };
  dfs(data);
  return res;
}
Copy the code

Depth first traversal

Depth-first traversal — starts at a vertex, visits that vertex first, finds the first unvisited neighbor that just visited that vertex, and then continues to find its next vertex to access. Repeat this step until all nodes are accessed.


Recursive writing of depth-first traversal

function deepTraversal(node) { 
  let nodes = [] 
  if(node ! =null) { 
    nodes.push(node) 
    let childrens = node.children 
    for (let i = 0; i < childrens.length; i++) {
      deepTraversal(childrens[i])
    }
  } 
  return nodes
}
Copy the code

Non-recursive writing of depth-first traversal

function deepTraversal(node) { 
  let nodes = [] 
  if(node ! =null) {
    let stack = []
    // Store the nodes to be accessed in the future
    stack.push(node)
    while(stack.length ! =0) { 
      let item = stack.pop()
      // The node being accessed
      nodes.push(item) 
      let childrens = item.children 
      for ( let i = childrens.length - 1; i >= 0; i--) {
        // Stack the children of the node at the current access point for future access.)
        stack.push(childrens[i])
      }
    }
  }
  return nodes
}
Copy the code

Breadth first traversal

Breadth-first traversal — starts at a vertex, visits that vertex first, finds all unvisited neighbors that have just visited that node, visits all of the first of those neighbors, and repeats until all of them have been accessed.

Recursive writing of breadth-first traversal

function wideTraversal(node) { 
  let nodes = [], i = 0
  if(node ! =null) { 
    nodes.push(node) 
    wideTraversal(node.nextElementSibling) 
    node = nodes[i++] 
    wideTraversal(node.firstElementChild)
  } 
  return nodes
}
Copy the code

Nonrecursive writing of breadth-first traversal

function wideTraversal(node) { 
  let nodes = [], i = 0 
  while(node ! =null) { 
    nodes.push(node) 
    node = nodes[i++] 
    let childrens = node.children 
    for (let i = 0; i < childrens.length; i++) { 
      nodes.push(childrens[i])
    }
  } 
  return nodes
}
Copy the code