2023过年前熟悉一下常见手写代码

2023过年前熟悉一下常见手写代码

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

toc

1. Array 篇

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

1.1 数组去重

1.1.1 使用 ES6 Set

js 复制代码
function unique(arr) {
  // return Array.from(new Set(arr))
  return [...new Set(arr)]
}

1.1.2 使用 ES6 Map

js 复制代码
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)

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

1.1.4 使用 filter + indexOf

js 复制代码
// 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

js 复制代码
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

js 复制代码
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

js 复制代码
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

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

1.3.2 实现 indexOf

js 复制代码
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

js 复制代码
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

js 复制代码
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为例,代码如下:

js 复制代码
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

js 复制代码
// 用处: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

js 复制代码
// 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

js 复制代码
// 用处: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 为例:

js 复制代码
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

js 复制代码
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

js 复制代码
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()处理

如下所示:

js 复制代码
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

js 复制代码
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

js 复制代码
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...

js 复制代码
// 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

js 复制代码
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

js 复制代码
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

js 复制代码
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

js 复制代码
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

js 复制代码
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

js 复制代码
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

js 复制代码
const sleep = ms => {
  return new Promise(resolve => {
    const timer = setTimeout(() => {
      resolve()
      clearTimeout(timer)
    }, ms)
  })
}

5.4 函数柯里化

js 复制代码
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 深拷贝

js 复制代码
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
}

本文由mdnice多平台发布

相关推荐
腾讯TNTWeb前端团队4 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰7 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪7 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪7 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy8 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom9 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom9 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom9 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom9 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom9 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试