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

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

本文主要总结了前端需要掌握的手写代码。分为 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
}

本文由mdnice多平台发布

原文链接:https://juejin.cn/post/7315304715809341440 作者:菲鸽

(0)
上一篇 2023年12月23日 下午4:05
下一篇 2023年12月23日 下午4:15

相关推荐

发表回复

登录后才能评论