【飞鸽传书】2022 年前端要掌握的手写代码

397 阅读10分钟

【飞鸽传书】2022 年前端要掌握的手写代码

本文主要总结了前端需要掌握的手写代码。分为 Arry 篇Object + Function 篇ES6 Set、Map、class 篇Promise 篇常用函数共 5 部分内容。

1. Array 篇

Array 篇比较简单,基础好的可以略过。

1.1 数组去重

1.1.1 使用 ES6 Set

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

1.1.2 使用 ES6 Map

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

本小节属于“新数组 + 方法系列”,可以把 map.has 改为 includes/indexOf 等,一样可以实现。

1.1.3 使用 reduce + (indexOf 或 includes)

// reduce + includes 为例
function unique(arr) {
  return arr.reduce((acc, item) => {
    if (!acc.includes(item)) {
      acc.push(item)
    }
    return acc
  }, [])
}

1.1.4 使用 filter + indexOf

// reduce + Map 为例
function unique(arr) {
  return arr.filter(function (item, index, arr) {
    return arr.indexOf(item) === index
  })
}

1.2 实现 Callback Methods 系列

该系列属于数组的回调函数系列,它们的参数都是固定的,有 2 类

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

第一类有every, some, map, filter, find, findIndex, forEach;

第二类有reducereduceRight

下面实现最具有代表性的 3 个方法:forEachfilterreduce

1.2.1 实现 forEach

Array.prototype._forEach = function (cb, thisArg) {
  if (typeof cb !== '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)
  }
}

1.2.2 实现 filter

Array.prototype._filter = function (cb, thisArg) {
  if (typeof cb !== 'function') {
    throw new TypeError(cb + ' is not a function')
  }
  const res = []
  for (var i = 0; i < this.length; i++) {
    // 回调函数执行为true
    if (cb.call(thisArg || window, this[i], i, this)) {
      res.push(this[i])
    }
  }
  return res
}

1.2.3 实现 reduce

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
  // 判断是否传入初始值
  if (initData === undefined) {
    // 没有初始值的空数组调用reduce会报错
    // 初始值赋值为数组第一个元素
    acc = this[0]
    i++
  } else {
    acc = initData
  }
  for (; i < this.length; i++) {
    // 计算结果赋值给初始值
    acc = cb(acc, this[i], i, this)
  }
  return acc
}

1.3 实现 基本 Methods 系列

这一类主要有这些方法:isArrayincludesindexOfjoinsliceflat

这里仅实现 isArrayindexOfjoinflat 作为例子。

1.3.1 实现 isArray

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

1.3.2 实现 indexOf

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
}

1.3.3 实现 join

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

1.3.4 实现 flat

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
}

2. Object + Function 篇

2.1 实现 keys、 values、 entries

entries为例,代码如下:

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
}

2.2 实现 Object.is

// 用处:Object.is(a, b),判断a是否等于b
// NaN相等,+0,-0不相等,如下:
// Object.is(NaN, NaN) ==> true
// Object.is(0,-0)     ==> false
// 原本的却是:
// NaN === NaN  ==> false
// 0 === -0     ==> true
// 1/0 === 1/-0 ==> false
Object._is = function (x, y) {
  if (x === y) {
    // 防止 -0 和 +0
    return x !== 0 || 1 / x === 1 / y
  }
  // 防止NaN
  return x !== x && y !== y
}

2.3 实现 Object.assign

// assign接收多个对象,并将多个对象合成一个对象
// 这些对象如果有重名属性,以后来的对象属性值为准
// assign返回一个对象,这个对象 === 第一个对象
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
}

2.4 实现 instanceof

// 用处:A instanceOf B,检测一个对象A是不是另一个对象B的实例的原理
// 查看对象B的prototype属性指向的原型对象是否在对象A的原型链上,若在则返回true,若不在则返回false。
const _instanceOf = function (obj, Fn) {
  if (typeof Fn !== 'function') {
    return false
  }
  if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) {
    // 非object或者function,统统返回false
    return false
  }
  let proto = Object.getPrototypeOf(obj)
  while (true) {
    if (proto === null) {
      return false
    }
    // 只有构造函数才有 prototype 属性
    if (proto === Fn.prototype) {
      return true
    }
    proto = Object.getPrototypeOf(proto)
  }
}

2.5 实现 call、 apply

call,apply 的实现类似,以 apply 为例:

Function.prototype._apply = function (context = window, args) {
  const fn = Symbol() // Symbol是唯一的,防止重名key
  context[fn] = this
  const result = context[fn](...args)
  delete context[fn] // 用完要删除
  return result
}

2.6 实现 new

const _new = function (Fn, ...args) {
  if (typeof Fn !== 'function') {
    throw new TypeError('_new function TypeError: the first param must be a function')
  }
  // const obj = {}
  // Object.setPrototypeOf(obj, Fn.prototype)
  // 上面2句可以由下面一句代替
  const obj = Object.create(Fn.prototype)
  const res = Fn.apply(obj, args)
  return typeof res === 'object' ? res : obj
}

2.7 实现 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))
  }
  // 修改返回函数的 prototype 为绑定函数的 prototype,实例就可以继承函数的原型中的值
  fbound.prototype = this.prototype
  return fbound
}

3. ES6 SetMap

在实现之前,要先了解 Object 常规对象的键的特性:

  • 基本类型,统统转化为字符串
  • 对象类型,统统 toString()处理

如下所示:

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 () {}

就是说:

  • 1)数字1与字符串'1'是不做区分的,布尔值true与字符串'true'也是不做区分的。(其他基础类型比较少用,不做研究,感兴趣的可以自己试验一下)
  • 2)引用类型,统统是经过 toString 处理后的值作为 key。这里要特别注意,一般的 Object 执行 toString 之后,得到的都是[object Object]

3.1 实现 ES6 Set

class Set {
  //Symbol.iterator 为每个对象定义了默认的迭代器。
  //该迭代器可以被for... of循环使用
  constructor(iterator = []) {
    //传递的对象必须是一个可迭代对象
    //所以需要判断传递的参数是否是可迭代对象
    if (typeof iterator[Symbol.iterator] !== 'function') {
      //不是可迭代对象就抛出一个错误
      throw new TypeError(`您所提供的 ${iterator}不是一个可迭代对象`)
    }
    //创建一个空数组
    this._datas = []
    //取出数组iterator里面的值,用for of循环
    for (const item of iterator) {
      // 将值添加到空数组中
      this.add(item)
    }
  }

  //判断两个值是否相等
  isEqual(data1, data2) {
    //1.存在两个都为0的情况
    if (data1 === 0 && data2 === 0) {
      return true
    }
    //2.Object.is()方法判断两个值是否为同一个值
    return Object.is(data1, data2)
  }

  //判断数据是否存在数组中
  has(data) {
    //遍历数组中的值(用for of)
    for (const item of this._datas) {
      //调用isEqual()方法判断 data(输入的数据)跟item(数组中的数据)
      if (this.isEqual(data, item)) {
        //相同返回true
        return true
      }
      //不相同返回false
      return false
    }
  }

  //添加数据的方法
  add(data) {
    //首先判断添加的值是否存在在当前数组中,存在的话就默认返回undefined,
    //不存在就把数据添加到之前定义的空数组中,
    // 此时已经不是空数组,而是存入了item值
    if (!this.has(data)) {
      //不存在就添加到数组中
      this._datas.push(data)
    }
    return this._datas
  }

  // 删除数据,返回结果true/false,删除成功/删除失败
  delete(data) {
    //遍历数组中的数据,i为下标,element为每个数据
    for (let i = 0; i < this._datas.length; i++) {
      const element = this._datas[i]
      //判断data跟element是否相同,相同说明数组中存在数据,可以删除
      if (this.isEqual(data, element)) {
        //删除数据利用splice()方法
        this._datas.splice(i, 1)
        //删除成功
        return true
      }
    }
    //删除失败
    return false
  }

  //清除数据
  clear() {
    //数组长度为0
    this._datas.length = 0
    return this._datas
  }

  //获取数组长度
  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() // 清空重新来
console.log(s.size) // 0
s.add(1)
console.log(s.size) // 1
s.add(1) // 检测重复
console.log(s.size) // 1
s.add('1') // 检测 数字1 与 字符串 '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

3.2 实现 ES6 Map

class Map {
  //Symbol.iterator 为每个对象定义了默认的迭代器。
  //该迭代器可以被for... of循环使用
  constructor(iterator = []) {
    //传递的对象必须是一个可迭代对象
    //所以需要判断传递的参数是否是可迭代对象
    if (typeof iterator[Symbol.iterator] !== 'function') {
      //不是可迭代对象就抛出一个错误
      throw new TypeError(`您所提供的 ${iterator}不是一个可迭代对象`)
    }
    //创建一个空数组
    this._datas = []
    //取出数组iterator里面的值,用for of循环
    for (const item of iterator) {
      const [k, v] = item
      // 将值添加到空数组中
      this.set(k, v)
    }
  }

  //判断两个值是否相等
  isEqual(data1, data2) {
    //1.存在两个都为0的情况
    if (data1 === 0 && data2 === 0) {
      return true
    }
    //2.Object.is()方法判断两个值是否为同一个值
    return Object.is(data1, data2)
  }

  //判断数据是否存在数组中
  has(key) {
    //遍历数组中的值(用for of)
    for (const [k, _] of this._datas) {
      //调用isEqual()方法判断 data(输入的数据)跟item(数组中的数据)
      if (this.isEqual(key, k)) {
        //相同返回true
        return true
      }
      //不相同返回false
      return false
    }
  }

  //添加数据的方法
  set(key, val) {
    //首先判断添加的值是否存在在当前数组中,存在的话就默认返回undefined,
    //不存在就把数据添加到之前定义的空数组中,
    // 此时已经不是空数组,而是存入了item值
    if (!this.has(key)) {
      //不存在就添加到数组中
      this._datas.push([key, val])
    } else {
      const item = this._datas.find(([k, _]) => k === key)
      item[1] = val
    }
    return this._datas
  }
  //添加数据的方法
  get(key) {
    //首先判断添加的值是否存在在当前数组中,存在的话就默认返回undefined,
    //不存在就把数据添加到之前定义的空数组中,
    // 此时已经不是空数组,而是存入了item值
    if (!this.has(key)) {
      //不存在就添加到数组中
      return undefined
    }
    const item = this._datas.find(([k, _]) => k === key)
    return item[1]
  }

  // 删除数据,返回结果true/false,删除成功/删除失败
  delete(key) {
    if (!this.has(key)) {
      //不存在返回false
      return false
    }
    const idx = this._datas.findIndex(([k, _]) => k === key)
    //删除数据利用splice()方法
    this._datas.splice(idx, 1)
    //删除成功,返回true
    return true
  }

  //清除数据
  clear() {
    //数组长度为0
    this._datas.length = 0
    return this._datas
  }

  //获取数组长度
  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()) // []

4. Promise 篇

4.1 实现 Promise

直接看一位大佬写的文章,一步一步带你实现符合 Promise A+ 规范的 Promise

ITEM 大佬: 从一道让我失眠的 Promise 面试题开始,深入分析 Promise 实现细节 juejin.cn/post/694531…

// MyPromise.js

// 先定义三个常量表示状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

// 新建 MyPromise 类
class MyPromise {
  constructor(executor) {
    // executor 是一个执行器,进入会立即执行
    // 并传入resolve和reject方法
    try {
      executor(this.resolve, this.reject)
    } catch (error) {
      this.reject(error)
    }
  }

  // 储存状态的变量,初始值是 pending
  status = PENDING
  // 成功之后的值
  value = null
  // 失败之后的原因
  reason = null

  // 存储成功回调函数
  onFulfilledCallbacks = []
  // 存储失败回调函数
  onRejectedCallbacks = []

  // 更改成功后的状态
  resolve = value => {
    // 只有状态是等待,才执行状态修改
    if (this.status === PENDING) {
      // 状态修改为成功
      this.status = FULFILLED
      // 保存成功之后的值
      this.value = value
      // resolve里面将所有成功的回调拿出来执行
      while (this.onFulfilledCallbacks.length) {
        // Array.shift() 取出数组第一个元素,然后()调用,shift不是纯函数,取出后,数组将失去该元素,直到数组为空
        this.onFulfilledCallbacks.shift()(value)
      }
    }
  }

  // 更改失败后的状态
  reject = reason => {
    // 只有状态是等待,才执行状态修改
    if (this.status === PENDING) {
      // 状态成功为失败
      this.status = REJECTED
      // 保存失败后的原因
      this.reason = reason
      // 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
          }

    // 为了链式调用这里直接创建一个 MyPromise,并在后面 return 出去
    const promise2 = new MyPromise((resolve, reject) => {
      const fulfilledMicrotask = () => {
        // 创建一个微任务等待 promise2 完成初始化
        queueMicrotask(() => {
          try {
            // 获取成功回调函数的执行结果
            const x = realOnFulfilled(this.value)
            // 传入 resolvePromise 集中处理
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      }

      const rejectedMicrotask = () => {
        // 创建一个微任务等待 promise2 完成初始化
        queueMicrotask(() => {
          try {
            // 调用失败回调,并且把原因返回
            const x = realOnRejected(this.reason)
            // 传入 resolvePromise 集中处理
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      }
      // 判断状态
      if (this.status === FULFILLED) {
        fulfilledMicrotask()
      } else if (this.status === REJECTED) {
        rejectedMicrotask()
      } else if (this.status === PENDING) {
        // 等待
        // 因为不知道后面状态的变化情况,所以将成功回调和失败回调存储起来
        // 等到执行成功失败函数的时候再传递
        this.onFulfilledCallbacks.push(fulfilledMicrotask)
        this.onRejectedCallbacks.push(rejectedMicrotask)
      }
    })

    return promise2
  }

  // resolve 静态方法
  static resolve(parameter) {
    // 如果传入 MyPromise 就直接返回
    if (parameter instanceof MyPromise) {
      return parameter
    }

    // 转成常规方式
    return new MyPromise(resolve => {
      resolve(parameter)
    })
  }

  // reject 静态方法
  static reject(reason) {
    return new MyPromise((resolve, reject) => {
      reject(reason)
    })
  }
}

function resolvePromise(promise2, x, resolve, reject) {
  // 如果相等了,说明return的是自己,抛出类型错误并返回
  if (promise2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  // 判断x是不是 MyPromise 实例对象
  if (x instanceof MyPromise) {
    // 执行 x,调用 then 方法,目的是将其状态变为 fulfilled 或者 rejected
    // x.then(value => resolve(value), reason => reject(reason))
    // 简化之后
    x.then(resolve, reject)
  } else {
    // 普通值
    resolve(x)
  }
}

// 作者:ITEM
// 链接:https://juejin.cn/post/6945319439772434469
// 来源:稀土掘金
// 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

4.2 实现 Promise.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)
            })
        })
    })
}

4.3 实现 Promise.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)
          }
        },
      )
    })
  })
}

4.4 实现 Promise.race

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

4.5 实现 Promise.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'))
          }
        },
      )
    })
  })
}

5. 常用函数

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()
  })
}

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)
    }
  })
}

5.3 Promisesleep

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

5.4 函数柯里化

const curry = fn => {
  const len = fn.length
  const judge = (...args1) => (args1.length >= len ? fn(...args1) : (...args2) => judge(...args1, ...args2))
  return judge
}
// 另一种
const curry = fn => {
  const len = fn.length //获取原函数的参数数量
  return function result(...args) {
    if (args.length < len) {
      //使用bind的用处是将此次的参数带到下一次result的参数里面,且不执行该函数
      return result.bind(null, ...args)
    } else {
      return fn(...args)
    }
  }
}

5.5 深拷贝

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 => {
  // 箭头函数直接返回自身
  if (!func.prototype) return func
  const bodyReg = /(?<={)(.|\n)+(?=})/m
  const paramReg = /(?<=\().+(?=\)\s+{)/
  const funcString = func.toString()
  // 分别匹配 函数参数 和 函数体
  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()) => {
  if (!isObject(target)) return target
  const type = getType(target)
  let cloneTarget
  if (!canTraverse[type]) {
    // 处理不能遍历的对象
    return handleNotTraverse(target, type)
  } else {
    // 这波操作相当关键,可以保证对象的原型不丢失!
    const ctor = target.constructor
    cloneTarget = new ctor()
  }

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

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

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

  // 处理数组和对象
  for (let prop in target) {
    if (target.hasOwnProperty(prop)) {
      cloneTarget[prop] = deepClone(target[prop], map)
    }
  }
  return cloneTarget
}