[Flying pigeon message] Handwritten code to master in 2022

This paper summarizes the front-end need to master the handwritten code. It is divided into five parts: Arry, Object + Function, ES6 Set, Map, class, Promise and common functions.

1. The Array

Array is relatively simple, the basic can be skipped.

1.1 Array deduplication

1.1.1 Using ES6 Set

function unique(arr) {
  // return Array.from(new Set(arr))
  return [...new Set(arr)]
}
Copy the code

1.1.2 Using ES6 Map

function unique(arr) {
  const map = new Map(a)const res = []
  arr.forEach(item= > {
    if(! map.has(item)) { map.set(item,true)
      res.push(item)
    }
  })
  return res
}
/ / use the reduce
function unique(arr) {
  const map = new Map(a)return arr.reduce((acc, item) = > {
    if(! map.has(item)){ map.set(item,true)
      acc.push(item)
    }
    return acc
  }, [])
}
Copy the code

This section belongs to “new array + method series”, you can change map.has to include /indexOf, etc.

1.1.3 Using Reduce + (indexOf or Includes)

// Reduce + includes is an example
function unique(arr) {
  return arr.reduce((acc, item) = > {
    if(! acc.includes(item)) { acc.push(item) }return acc
  }, [])
}

Copy the code

1.1.4 Using Filter + indexOf

// Reduce + Map is an example
function unique(arr) {
  return arr.filter(function (item, index, arr) {
    return arr.indexOf(item) === index
  })
}
Copy the code

1.2 implementationCallback MethodsA series of

This family belongs to the array callback function family, which takes fixed arguments and has two classes

  • f(cb(ele, idx, arr), thisArg)
  • f(cb(acc, ele, idx, arr), initData)

The first class has every, some, map, filter, find, findIndex, forEach;

The second category includes reduce and reduceRight

The following are the three most representative methods: forEach, Filter and Reduce

1.2.1 implementationforEach

Array.prototype._forEach = function (cb, thisArg) {
  if (typeofcb ! = ='function') {
    throw new TypeError(cb + ' is not a function')}for (var i = 0; i < this.length; i++) {
    cb.call(thisArg || window.this[i], i, this)}}Copy the code

1.2.2 implementationfilter

Array.prototype._filter = function (cb, thisArg) {
  if (typeofcb ! = ='function') {
    throw new TypeError(cb + ' is not a function')}const res = []
  for (var i = 0; i < this.length; i++) {
    // The callback function executes true
    if (cb.call(thisArg || window.this[i], i, this)) {
      res.push(this[i])
    }
  }
  return res
}
Copy the code

1.2.3 implementationreduce

Array.prototype._reduce = function (cb, initData) {
  if (this.length === 0) {
    throw new Error('Reduce of empty array with no initial value')}let i = 0
  let acc
  // Determine whether an initial value is passed in
  if (initData === undefined) {
    // An error is reported when calling reduce with an empty array with no initial value
    // Assign the initial value to the first element of the array
    acc = this[0]
    i++
  } else {
    acc = initData
  }
  for (; i < this.length; i++) {
    // The computed result is assigned to the initial value
    acc = cb(acc, this[i], i, this)}return acc
}
Copy the code

1.3 implementationBasic MethodsA series of

This category includes isArray, includes, indexOf, Join, slice, and Flat.

Here only isArray, indexOf, Join and Flat are implemented as examples.

1.3.1 implementationisArray

Array._isArray = function (obj) {
  return Object.prototype.toString.call(obj).slice(8, -1) = = ='Array'
}
Copy the code

1.3.2 implementationindexOf

Array.prototype._indexOf = function (val, beginIndex = 0) {
  if (this.length < 1 || beginIndex > this.length) {
    return -1
  }
  beginIndex = beginIndex <= 0 ? 0 : beginIndex
  for (let i = beginIndex; i < this.length; i++) {
    if (this[i] === val) return i
  }
  return -1
}
Copy the code

1.3.3 implementationjoin

Array.prototype._join = function (sep = ', ') {
  if (!this.length) {
    return ' '
  }
  let str = this[0].toString()
  for (let i = 1; i < this.length; i++) {
    str += `${sep}The ${this[i].toString()}`
  }
  return str
}
Copy the code

1.3.4 implementationflat

Array.prototype._flat = function (depth = 1) {
  const res = []
  for (let i = 0; i < this.length; i++) {
    const item = this[i]
    if (Array.isArray(item) && depth > 0) {
      depth -= 1
      res = res.concat(item._flat(depth))
    } else {
      res = res.concat(item)
    }
  }
  return res
}
Copy the code

2. Object + Function

2.1 Implement keys, values and entries

In the case of entries, the code is as follows:

Object.prototype._entries = function (obj) {
  const res = []
  const keys = Reflect.ownKeys(obj)
  for (let i = 0; i < keys.length; i++) {
    res.push([keys[i], obj[keys[i]]])
  }
  return res
}
Copy the code

2.2 implementation Object. Is

// use: object. is(a, b), check whether a is equal to b
// NaN is equal, +0, -0 is not equal, as follows:
// Object.is(NaN, NaN) ==> true
// Object.is(0,-0) ==> false
// The original is:
// NaN === NaN ==> false
// 0 === -0 ==> true
// 1/0 === 1/-0 ==> false
Object._is = function (x, y) {
  if (x === y) {
    // Prevent -0 and +0
    returnx ! = =0 || 1 / x === 1 / y
  }
  / / prevent NaN
  returnx ! == x && y ! == y }Copy the code

2.3 implementation Object. The assign

// Assign takes multiple objects and combines them into one object
// If these objects have the same name attribute, the subsequent object attribute value will prevail
// Assign returns an object, which === the first object
Object._assign = function (target, ... args) {
  if (target === null || target === undefined) {
    throw new TypeError('Cannot convert undefined or null to object')
  }
  target = Object(target)
  for (let nextObj of args) {
    for (let key in nextObj) {
      if (nextObj.hasOwnProperty(key)) {
        target[key] = nextObj[key]
      }
    }
  }
  return target
}
Copy the code

2.4 implementation instanceof

// use: an instanceOf B, which checks whether an object A is an instanceOf another object B
// Check whether the prototype object pointed to by the prototype property of object B is on the prototype chain of object A. If so, return true; if not, return false.
const _instanceOf = function (obj, Fn) {
  if (typeofFn ! = ='function') {
    return false
  }
  if (obj === null| | -typeofobj ! = ='object' && typeofobj ! = ='function')) {
    // If not object or function, return false
    return false
  }
  let proto = Object.getPrototypeOf(obj)
  while (true) {
    if (proto === null) {
      return false
    }
    // Only constructors have the prototype attribute
    if (proto === Fn.prototype) {
      return true
    }
    proto = Object.getPrototypeOf(proto)
  }
}
Copy the code

2.5 Implement Call and apply

The implementation of call and apply is similar. Take Apply as an example:

Function.prototype._apply = function (context = window, args) {
  const fn = Symbol(a)// Symbol is unique, preventing duplicate keys
  context[fn] = this
  constresult = context[fn](... args)delete context[fn] // Delete when used
  return result
}
Copy the code

2.6 implementation of new

const _new = function (Fn, ... args) {
  if (typeofFn ! = ='function') {
    throw new TypeError('_new function TypeError: the first param must be a function')}// const obj = {}
  // Object.setPrototypeOf(obj, Fn.prototype)
  // The above two sentences can be replaced by the following one
  const obj = Object.create(Fn.prototype)
  const res = Fn.apply(obj, args)
  return typeof res === 'object' ? res : obj
}
Copy the code

2.7 implement the bind

Function.prototype._bind = function (context = window. args) {
  if (typeof this! = ='function') {
    throw new Error('Function.prototype.bind - what is trying to be bound is not callable')}const self = this
  const fbound = function (. innerArgs) {
    self.apply(this instanceof self ? this : context, args.concat(innerArgs))
  }
  // Change the return function's prototype to the binding function's prototype, and the instance can inherit the function's prototype value
  fbound.prototype = this.prototype
  return fbound
}
Copy the code

3. ES6 Set,Map

Before implementing this, we should first understand the key characteristics of the regular Object:

  • Primitive types, all converted to strings
  • Object types are handled by toString()

As follows:

const o = {}
o[1] = 1
console.log(o) // {'1': 1}
o['1'] = 100
console.log(o) // {'1': 100}
o[true] = 2
console.log(o) // { '1': 100, true: 2 }
o['true'] = 200
console.log(o) // { '1': 100, true: 200 }
o[{}] = 3
console.log(o) // { '1': 100, true: 200, '[object Object]': 3 }
o[{ name: 'flygo' }] = 300
console.log(o) // { '1': 100, true: 200, '[object Object]': 300 }
o[[]] = 4
console.log(o) // { '1': 100, true: 200, '[object Object]': 300, '': 4 }
o[[1]] = 4
console.log(o) // { '1': 100, true: 200, '[object Object]': 300, '': 4 }
o[[1.2.3]] = 4
console.log(o) / / {' 1 ': 4, true: 200,' [object object] ': 300,' ', 4, '1, 2, 3, 4}
o[function () = {}]5
console.log(o) / / {' 1 ': 4, true: 200,' [object object] ': 300,' ', 4, '1, 2, 3, 4,' function () {} ': 5}
o[
  function () {
    return 'flygo'}] =500
console.log(o) / / = >
/ * {' 1 ': 4, true: 200,' [object object] ': 300,' ', 4, '1, 2, 3, 4,' function () {} ': 5, "function () {\r\n return 'flygo'\r\n }": 5 } */
console.log({}.toString()) // [object Object]
console.log({ name: 'flygo' }.toString()) // [object Object]
console.log([].toString()) //
console.log([1].toString()) / / 1
console.log([1.2.3].toString()) / / 1, 2, 3
console.log(function () {}.toString()) // function () {}

Copy the code

That is to say:

  • 1) digital1With the string'1'No distinction, BooleantrueWith the string'true'There is no distinction. (Other basic types are less used, do not do research, interested in your own experiment)
  • 2) Reference types, all are passedtoStringThe value after processing askey. I want to be very careful here, in generalObjectperformtoStringAfter that, I get all of these[object Object].

3.1 implementationES6 Set

class Set {
  Iterator defines the default iterator for each object.
  // This iterator can be used for... Of recycling
  constructor(iterator = []) {
    // The object passed must be an iterable
    // So we need to check whether the argument passed is an iterable
    if (typeof iterator[Symbol.iterator] ! = ='function') {
      Throw an error if it is not an iterable
      throw new TypeError('What you have provided${iterator}Is not an iterable)}// Create an empty array
    this._datas = []
    // Get the values in the array iterator with the for of loop
    for (const item of iterator) {
      // Add the value to the empty array
      this.add(item)
    }
  }

  // Check whether two values are equal
  isEqual(data1, data2) {
    //1. There are two cases where both are 0
    if (data1 === 0 && data2 === 0) {
      return true
    }
    // the 2.object.is () method checks whether two values are the same
    return Object.is(data1, data2)
  }

  // Check whether the data exists in the array
  has(data) {
    // Go through the list of values (for of)
    for (const item of this._datas) {
      // Call isEqual() to determine whether data(the input data) is the same as item(the array data).
      if (this.isEqual(data, item)) {
        // Same returns true
        return true
      }
      // If not, return false
      return false}}// The method to add data
  add(data) {
    // If the added value exists in the current array, undefined is returned by default.
    // Add data to the previously defined empty array if it does not exist.
    // Instead of an empty array, the item value is stored
    if (!this.has(data)) {
      // Add it to array if it doesn't exist
      this._datas.push(data)
    }
    return this._datas
  }

  // Delete data, return true/false, delete succeeded/delete failed
  delete(data) {
    // Iterate over the data in the number group, with I as the subscript and element as each data
    for (let i = 0; i < this._datas.length; i++) {
      const element = this._datas[i]
      // Check whether data and Element are the same. If they are the same, there is data in the array and it can be deleted
      if (this.isEqual(data, element)) {
        // Delete data using the splice() method
        this._datas.splice(i, 1)
        // The deletion succeeded
        return true}}// Delete failed
    return false
  }

  // Clear data
  clear() {
    // The array length is 0
    this._datas.length = 0
    return this._datas
  }

  // Get the array length
  get size() {
    return this._datas.length
  }

  //forEach (for of)
  forEach(callback) {
    for (const item of this._datas) {
      callback(item, item, this)}}values() {
    return this._datas
  }
  entries() {
    return this._datas.map(item= > [item, item])
  }

  //*[Sysbol.iterator]* [Symbol.iterator]() {
    for (const item of this._datas) {
      yield item
    }
  }
}

const s = new Set([1.1.'1'])
console.log([...s]) // [1, '1']
console.log(s.size) / / 2
s.clear() // Clear and start again
console.log(s.size) / / 0
s.add(1)
console.log(s.size) / / 1
s.add(1) // Check duplicates
console.log(s.size) / / 1
s.add('1') // Check the number 1 and string '1'
console.log(s.size) / / 2
console.log(s.values()) // [1, '1']
s.add(2)
console.log(s.size) / / 3
console.log(s.values()) //[1, '1', 2]
console.log(s.entries()) // [[1, 1], ['1', '1'], [2, 2]]
console.log([...s]) // [1, '1', 2]
s.delete(1)
console.log(s.size) / / 2
s.clear()
console.log(s.size) / / 0

Copy the code

3.2 implementationES6 Map

class Map {
  Iterator defines the default iterator for each object.
  // This iterator can be used for... Of recycling
  constructor(iterator = []) {
    // The object passed must be an iterable
    // So we need to check whether the argument passed is an iterable
    if (typeof iterator[Symbol.iterator] ! = ='function') {
      Throw an error if it is not an iterable
      throw new TypeError('What you have provided${iterator}Is not an iterable)}// Create an empty array
    this._datas = []
    // Get the values in the array iterator with the for of loop
    for (const item of iterator) {
      const [k, v] = item
      // Add the value to the empty array
      this.set(k, v)
    }
  }

  // Check whether two values are equal
  isEqual(data1, data2) {
    //1. There are two cases where both are 0
    if (data1 === 0 && data2 === 0) {
      return true
    }
    // the 2.object.is () method checks whether two values are the same
    return Object.is(data1, data2)
  }

  // Check whether the data exists in the array
  has(key) {
    // Go through the list of values (for of)
    for (const [k, _] of this._datas) {
      // Call isEqual() to determine whether data(the input data) is the same as item(the array data).
      if (this.isEqual(key, k)) {
        // Same returns true
        return true
      }
      // If not, return false
      return false}}// The method to add data
  set(key, val) {
    // If the added value exists in the current array, undefined is returned by default.
    // Add data to the previously defined empty array if it does not exist.
    // Instead of an empty array, the item value is stored
    if (!this.has(key)) {
      // Add it to array if it doesn't exist
      this._datas.push([key, val])
    } else {
      const item = this._datas.find(([k, _]) = > k === key)
      item[1] = val
    }
    return this._datas
  }
  // The method to add data
  get(key) {
    // If the added value exists in the current array, undefined is returned by default.
    // Add data to the previously defined empty array if it does not exist.
    // Instead of an empty array, the item value is stored
    if (!this.has(key)) {
      // Add it to array if it doesn't exist
      return undefined
    }
    const item = this._datas.find(([k, _]) = > k === key)
    return item[1]}// Delete data, return true/false, delete succeeded/delete failed
  delete(key) {
    if (!this.has(key)) {
      // Return false if no exists
      return false
    }
    const idx = this._datas.findIndex(([k, _]) = > k === key)
    // Delete data using the splice() method
    this._datas.splice(idx, 1)
    // Delete successfully, return true
    return true
  }

  // Clear data
  clear() {
    // The array length is 0
    this._datas.length = 0
    return this._datas
  }

  // Get the array length
  get size() {
    return this._datas.length
  }

  //forEach (for of)
  forEach(callback) {
    for (const [k, v] of this._datas) {
      callback(v, k, this)}}keys() {
    return this._datas.reduce((acc, cur) = > {
      acc.push(cur[0])
      return acc
    }, [])
  }
  values() {
    return this._datas.reduce((acc, cur) = > {
      acc.push(cur[1])
      return acc
    }, [])
  }
  entries() {
    return this._datas.reduce((acc, cur) = > {
      acc.push([cur[0], cur[1]])
      return acc
    }, [])
  }

  //*[Sysbol.iterator]* [Symbol.iterator]() {
    for (const item of this._datas) {
      yield item
    }
  }
}

const m = new Map([[1], [2.3]])
console.log([...m]) // [ [ 1, undefined ], [ 2, 3 ] ]
console.log(m.keys()) // [1, 2]
console.log(m.values()) // [ undefined, 3 ]
console.log(m.entries()) // [ [ 1, undefined ], [ 2, 3 ] ]
console.log(m.size) // [ [ 1, undefined ], [ 2, 3 ] ]
m.clear()
m.set(1.2)
console.log(m.entries()) // [[1, 2]]
m.set(1.3)
console.log(m.entries()) // [[1, 3]]
m.delete(1)
console.log(m.entries()) / / []
Copy the code

4. Promise

4.1 implementationPromise

Directly see A big guy wrote the article, step by step with you to achieve in line with the Promise A+ specification Promise!

ITEM: Starting with a Promise interview question that made me lose sleep, in-depth analysis of the Promise implementation details juejin.cn/post/694531…

// MyPromise.js

// Define three constants to represent the state
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

// Create the MyPromise class
class MyPromise {
  constructor(executor) {
    // Executor is an executor that executes immediately upon entry
    // Pass in the resolve and reject methods
    try {
      executor(this.resolve, this.reject)
    } catch (error) {
      this.reject(error)
    }
  }

  // Store the state variable. The initial value is pending
  status = PENDING
  // Value after success
  value = null
  // The cause of the failure
  reason = null

  // Store the success callback function
  onFulfilledCallbacks = []
  // Store the failed callback function
  onRejectedCallbacks = []

  // Change the status after success
  resolve = value= > {
    // State changes are performed only when the state is wait
    if (this.status === PENDING) {
      // Status changed to successful
      this.status = FULFILLED
      // Save the value after success
      this.value = value
      // All successful callbacks are executed in resolve
      while (this.onFulfilledCallbacks.length) {
        Shift () takes the first element of the Array, and then () calls it. Shift is not a pure function
        this.onFulfilledCallbacks.shift()(value)
      }
    }
  }

  // Change the failed state
  reject = reason= > {
    // State changes are performed only when the state is wait
    if (this.status === PENDING) {
      // Status success is failure
      this.status = REJECTED
      // Cause of save failure
      this.reason = reason
      // All failed callbacks are executed in resolve
      while (this.onRejectedCallbacks.length) {
        this.onRejectedCallbacks.shift()(reason)
      }
    }
  }

  then(onFulfilled, onRejected) {
    const realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value= > value
    const realOnRejected =
      typeof onRejected === 'function'
        ? onRejected
        : reason= > {
            throw reason
          }

    // Create a MyPromise for the chained call and return it
    const promise2 = new MyPromise((resolve, reject) = > {
      const fulfilledMicrotask = () = > {
        // Create a microtask and wait for the initialization of promise2 to complete
        queueMicrotask(() = > {
          try {
            // Get the result of the successful callback
            const x = realOnFulfilled(this.value)
            // Pass in resolvePromise
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      }

      const rejectedMicrotask = () = > {
        // Create a microtask and wait for the initialization of promise2 to complete
        queueMicrotask(() = > {
          try {
            // Call the failed callback and return the reason
            const x = realOnRejected(this.reason)
            // Pass in resolvePromise
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      }
      // Determine the status
      if (this.status === FULFILLED) {
        fulfilledMicrotask()
      } else if (this.status === REJECTED) {
        rejectedMicrotask()
      } else if (this.status === PENDING) {
        / / wait for
        Success and failure callbacks are stored because we do not know how the state will change later
        // Wait until the function succeeds or fails
        this.onFulfilledCallbacks.push(fulfilledMicrotask)
        this.onRejectedCallbacks.push(rejectedMicrotask)
      }
    })

    return promise2
  }

  // resolve static method
  static resolve(parameter) {
    // If you pass MyPromise, return it directly
    if (parameter instanceof MyPromise) {
      return parameter
    }

    // Switch to normal mode
    return new MyPromise(resolve= > {
      resolve(parameter)
    })
  }

  // reject static methods
  static reject(reason) {
    return new MyPromise((resolve, reject) = > {
      reject(reason)
    })
  }
}

function resolvePromise(promise2, x, resolve, reject) {
  // If it is equal, return itself, throws a type error, and returns
  if (promise2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))}// Determine if x is the MyPromise instance object
  if (x instanceof MyPromise) {
    // Execute x and call the then method, with the purpose of changing its state to pity or Rejected
    // x.then(value => resolve(value), reason => reject(reason))
    // After simplification
    x.then(resolve, reject)
  } else {
    / / common values
    resolve(x)
  }
}

// Author: ITEM
/ / link: https://juejin.cn/post/6945319439772434469
// Source: Rare earth nuggets
// The copyright belongs to the author. Commercial reprint please contact the author for authorization, non-commercial reprint please indicate the source.
Copy the code

4.2 implementationPromise.all

Promise._all = function PromiseAll(promises){
    return new Promise((resolve, reject) = >{
        if(!Array.isArray(promises)){
            throw new TypeError("promises must be an array")}const result = []
        let count = 0
        promises.forEach((promise, index) = > {
            promise.then((res) = >{
                result[index] = res
                count++
                count === promises.length && resolve(result)
            }, (err) = >{
                reject(err)
            })
        })
    })
}
Copy the code

4.3 implementationPromise.allSettled

Promise._allSettled = function allSettled(promises) {
  if (promises.length === 0) return Promise.resolve([])

  const _promises = promises.map(item= > (item instanceof Promise ? item : Promise.resolve(item)))

  return new Promise((resolve, reject) = > {
    const result = []
    let unSettledPromiseCount = _promises.length

    _promises.forEach((promise, index) = > {
      promise.then(
        value= > {
          result[index] = {
            status: 'fulfilled',
            value,
          }

          unSettledPromiseCount -= 1
          // resolve after all are settled
          if (unSettledPromiseCount === 0) {
            resolve(result)
          }
        },
        reason= > {
          result[index] = {
            status: 'rejected',
            reason,
          }

          unSettledPromiseCount -= 1
          // resolve after all are settled
          if (unSettledPromiseCount === 0) {
            resolve(result)
          }
        },
      )
    })
  })
}
Copy the code

4.4 implementationPromise.race

Promise._race = function promiseRace(promiseArr) {
  return new Promise((resolve, reject) = > {
    promiseArr.forEach(p= > {
      Promise.resolve(p).then(
        val= > {
          resolve(val)
        },
        err= > {
          reject(err)
        },
      )
    })
  })
}
Copy the code

4.5 implementationPromise.any

Promise._any = function promiseAny(promiseArr) {
  let index = 0
  return new Promise((resolve, reject) = > {
    if (promiseArr.length === 0) return
    promiseArr.forEach((p, i) = > {
      Promise.resolve(p).then(
        val= > {
          resolve(val)
        },
        err= > {
          index++
          if (index === promiseArr.length) {
            reject(new Error('All promises were rejected'))}},)})})}Copy the code

5. Common functions

5.1 PromiseAjax

const ajax = ({ url = null, method = 'GET'.async = true }) = > {
  return new Promise((resolve, reject) = > {
    let xhr = new XMLHttpRequest()
    xhr.open(method, url, async)
    xhr.onreadystatechange = () = > {
      if (xhr.status < 200 || xhr.status >= 400) return
      if (xhr.readyState === 4) {
        let result = xhr.responseText
        resolve(result)
      }
    }
    xhr.onerror = err= > {
      reject(err)
    }
    xhr.send()
  })
}
Copy the code

5.2 Promisejsonp

const jsonp = ({ url, params, callbackName }) = > {
  const generateUrl = () = > {
    let dataSrc = ' '
    for (let key in params) {
      if (params.hasOwnProperty(key)) {
        dataSrc += `${key}=${params[key]}& `
      }
    }
    dataSrc += `callback=${callbackName}`
    return `${url}?${dataSrc}`
  }
  return new Promise((resolve, reject) = > {
    const scriptEle = document.createElement('script')
    scriptEle.src = generateUrl()
    document.body.appendChild(scriptEle)
    window[callbackName] = data= > {
      resolve(data)
      document.removeChild(scriptEle)
    }
  })
}
Copy the code

5.3 Promisesleep

const sleep = ms= > {
  return new Promise(resolve= > {
    const timer = setTimeout(() = > {
      resolve()
      clearTimeout(timer)
    }, ms)
  })
}
Copy the code

5.4 Currization of functions

const curry = fn= > {
  const len = fn.length
  const judge = (. args1) = >(args1.length >= len ? fn(... args1) :(. args2) = >judge(... args1, ... args2))return judge
}
/ / another
const curry = fn= > {
  const len = fn.length // Get the number of arguments of the original function
  return function result(. args) {
    if (args.length < len) {
      // Bind is used to take this argument to the next result argument without executing that function
      return result.bind(null. args) }else {
      returnfn(... args) } } }Copy the code

5.5 deep copy

const getType = obj= > Object.prototype.toString.call(obj)

const isObject = target= > (typeof target === 'object' || typeof target === 'function') && target ! = =null

const canTraverse = {
  '[object Map]': true.'[object Set]': true.'[object Array]': true.'[object Object]': true.'[object Arguments]': true,}const mapTag = '[object Map]'
const setTag = '[object Set]'
const boolTag = '[object Boolean]'
const numberTag = '[object Number]'
const stringTag = '[object String]'
const symbolTag = '[object Symbol]'
const dateTag = '[object Date]'
const errorTag = '[object Error]'
const regexpTag = '[object RegExp]'
const funcTag = '[object Function]'

const handleRegExp = target= > {
  const { source, flags } = target
  return new target.constructor(source, flags)
}

const handleFunc = func= > {
  // The arrow function returns itself directly
  if(! func.prototype)return func
  const bodyReg = / (? <={)(.|\n)+(? =})/m
  const paramReg = / (? < = \ () + (? =\)\s+{)/
  const funcString = func.toString()
  // Match function parameters and function body respectively
  const param = paramReg.exec(funcString)
  const body = bodyReg.exec(funcString)
  if(! body)return null
  if (param) {
    const paramArr = param[0].split(', ')
    return new Function(... paramArr, body[0])}else {
    return new Function(body[0])}}const handleNotTraverse = (target, tag) = > {
  const Ctor = target.constructor
  switch (tag) {
    case boolTag:
      return new Object(Boolean.prototype.valueOf.call(target))
    case numberTag:
      return new Object(Number.prototype.valueOf.call(target))
    case stringTag:
      return new Object(String.prototype.valueOf.call(target))
    case symbolTag:
      return new Object(Symbol.prototype.valueOf.call(target))
    case errorTag:
    case dateTag:
      return new Ctor(target)
    case regexpTag:
      return handleRegExp(target)
    case funcTag:
      return handleFunc(target)
    default:
      return new Ctor(target)
  }
}

const deepClone = (target, map = new WeakMap(a)) = > {
  if(! isObject(target))return target
  const type = getType(target)
  let cloneTarget
  if(! canTraverse[type]) {// Process objects that cannot be traversed
    return handleNotTraverse(target, type)
  } else {
    // This operation is critical to ensure that the object's prototype is not lost!
    const ctor = target.constructor
    cloneTarget = new ctor()
  }

  if (map.get(target)) return target
  map.set(target, true)

  if (type === mapTag) {
    / / the Map processing
    target.forEach((item, key) = > {
      cloneTarget.set(deepClone(key, map), deepClone(item, map))
    })
  }

  if (type === setTag) {
    / / handle the Set
    target.forEach(item= > {
      cloneTarget.add(deepClone(item, map))
    })
  }

  // Handle arrays and objects
  for (let prop in target) {
    if (target.hasOwnProperty(prop)) {
      cloneTarget[prop] = deepClone(target[prop], map)
    }
  }
  return cloneTarget
}
Copy the code