关于javascript:前端二面手写面试题总结

36次阅读

共计 30985 个字符,预计需要花费 78 分钟才能阅读完成。

给定两个数组,写一个办法来计算它们的交加

例如:给定 nums1 = [1, 2, 2, 1],nums2 = [2, 2],返回 [2, 2]。

function union (arr1, arr2) {
  return arr1.filter(item => {return arr2.indexOf(item) > - 1;
  })
}
 const a = [1, 2, 2, 1];
 const b = [2, 3, 2];
 console.log(union(a, b)); // [2, 2]

手写 Promise.then

then 办法返回一个新的 promise 实例,为了在 promise 状态发生变化时(resolve / reject 被调用时)再执行 then 里的函数,咱们应用一个 callbacks 数组先把传给 then 的函数暂存起来,等状态扭转时再调用。

那么,怎么保障后一个 **then** 里的办法在前一个 **then**(可能是异步)完结之后再执行呢? 咱们能够将传给 then 的函数和新 promiseresolve 一起 push 到前一个 promisecallbacks 数组中,达到承前启后的成果:

  • 承前:以后一个 promise 实现后,调用其 resolve 变更状态,在这个 resolve 里会顺次调用 callbacks 里的回调,这样就执行了 then 里的办法了
  • 启后:上一步中,当 then 里的办法执行实现后,返回一个后果,如果这个后果是个简略的值,就间接调用新 promiseresolve,让其状态变更,这又会顺次调用新 promisecallbacks 数组里的办法,周而复始。。如果返回的后果是个 promise,则须要等它实现之后再触发新 promiseresolve,所以能够在其后果的 then 里调用新 promiseresolve
then(onFulfilled, onReject){
    // 保留前一个 promise 的 this
    const self = this; 
    return new MyPromise((resolve, reject) => {
      // 封装前一个 promise 胜利时执行的函数
      let fulfilled = () => {
        try{const result = onFulfilled(self.value); // 承前
          return result instanceof MyPromise? result.then(resolve, reject) : resolve(result); // 启后
        }catch(err){reject(err)
        }
      }
      // 封装前一个 promise 失败时执行的函数
      let rejected = () => {
        try{const result = onReject(self.reason);
          return result instanceof MyPromise? result.then(resolve, reject) : reject(result);
        }catch(err){reject(err)
        }
      }
      switch(self.status){
        case PENDING: 
          self.onFulfilledCallbacks.push(fulfilled);
          self.onRejectedCallbacks.push(rejected);
          break;
        case FULFILLED:
          fulfilled();
          break;
        case REJECT:
          rejected();
          break;
      }
    })
   }

留神:

  • 间断多个 then 里的回调办法是同步注册的,但注册到了不同的 callbacks 数组中,因为每次 then 都返回新的 promise 实例(参考下面的例子和图)
  • 注册实现后开始执行构造函数中的异步事件,异步实现之后顺次调用 callbacks 数组中提前注册的回调

实现每隔一秒打印 1,2,3,4

// 应用闭包实现
for (var i = 0; i < 5; i++) {(function(i) {setTimeout(function() {console.log(i);
    }, i * 1000);
  })(i);
}
// 应用 let 块级作用域
for (let i = 0; i < 5; i++) {setTimeout(function() {console.log(i);
  }, i * 1000);
}

实现非负大整数相加

JavaScript 对数值有范畴的限度,限度如下:

Number.MAX_VALUE // 1.7976931348623157e+308
Number.MAX_SAFE_INTEGER // 9007199254740991
Number.MIN_VALUE // 5e-324
Number.MIN_SAFE_INTEGER // -9007199254740991

如果想要对一个超大的整数 (> Number.MAX_SAFE_INTEGER) 进行加法运算,然而又想输入个别模式,那么应用 + 是无奈达到的,一旦数字超过 Number.MAX_SAFE_INTEGER 数字会被立刻转换为迷信计数法,并且数字精度相比以前将会有误差。

实现一个算法进行大数的相加:

function sumBigNumber(a, b) {
  let res = '';
  let temp = 0;

  a = a.split('');
  b = b.split('');

  while (a.length || b.length || temp) {temp += ~~a.pop() + ~~b.pop();
    res = (temp % 10) + res;
    temp  = temp > 9
  }
  return res.replace(/^0+/, '');
}

其次要的思路如下:

  • 首先用字符串的形式来保留大数,这样数字在数学示意上就不会发生变化
  • 初始化 res,temp 来保留两头的计算结果,并将两个字符串转化为数组,以便进行每一位的加法运算
  • 将两个数组的对应的位进行相加,两个数相加的后果可能大于 10,所以可能要仅为,对 10 进行取余操作,将后果保留在以后位
  • 判断以后位是否大于 9,也就是是否会进位,若是则将 temp 赋值为 true,因为在加法运算中,true 会主动隐式转化为 1,以便于下一次相加
  • 反复上述操作,直至计算完结

将数字每千分位用逗号隔开

数字有小数版本:

let format = n => {let num = n.toString() // 转成字符串
    let decimals = ''
        // 判断是否有小数
    num.indexOf('.') > -1 ? decimals = num.split('.')[1] : decimals
    let len = num.length
    if (len <= 3) {return num} else {
        let temp = ''
        let remainder = len % 3
        decimals ? temp = '.' + decimals : temp
        if (remainder > 0) { // 不是 3 的整数倍
            return num.slice(0, remainder) + ',' + num.slice(remainder, len).match(/\d{3}/g).join(',') + temp
        } else { // 是 3 的整数倍
            return num.slice(0, len).match(/\d{3}/g).join(',') + temp 
        }
    }
}
format(12323.33)  // '12,323.33'

数字无小数版本:

let format = n => {let num = n.toString() 
    let len = num.length
    if (len <= 3) {return num} else {
        let remainder = len % 3
        if (remainder > 0) { // 不是 3 的整数倍
            return num.slice(0, remainder) + ',' + num.slice(remainder, len).match(/\d{3}/g).join(',') 
        } else { // 是 3 的整数倍
            return num.slice(0, len).match(/\d{3}/g).join(',') 
        }
    }
}
format(1232323)  // '1,232,323'

Function.prototype.bind

Function.prototype.bind = function(context, ...args) {if (typeof this !== 'function') {throw new Error("Type Error");
  }
  // 保留 this 的值
  var self = this;

  return function F() {
    // 思考 new 的状况
    if(this instanceof F) {return new self(...args, ...arguments)
    }
    return self.apply(context, [...args, ...arguments])
  }
}

模板引擎实现

let template = '我是{{name}},年龄{{age}},性别{{sex}}';
let data = {
  name: '姓名',
  age: 18
}
render(template, data); // 我是姓名,年龄 18,性别 undefined

function render(template, data) {const reg = /\{\{(\w+)\}\}/; // 模板字符串正则
  if (reg.test(template)) { // 判断模板里是否有模板字符串
    const name = reg.exec(template)[1]; // 查找以后模板里第一个模板字符串的字段
    template = template.replace(reg, data[name]); // 将第一个模板字符串渲染
    return render(template, data); // 递归的渲染并返回渲染后的构造
  }
  return template; // 如果模板没有模板字符串间接返回
}

参考:前端手写面试题具体解答

实现 Promise

var PromisePolyfill = (function () {
  // 和 reject 不同的是 resolve 须要尝试开展 thenable 对象
  function tryToResolve (value) {if (this === value) {
    // 次要是避免上面这种状况
    // let y = new Promise(res => setTimeout(res(y)))
      throw TypeError('Chaining cycle detected for promise!')
    }

    // 依据标准 2.32 以及 2.33 对对象或者函数尝试开展
    // 保障 S6 之前的 polyfill 也能和 ES6 的原生 promise 混用
    if (value !== null &&
      (typeof value === 'object' || typeof value === 'function')) {
      try {
      // 这里记录这次 then 的值同时要被 try 包裹
      // 次要起因是 then 可能是一个 getter, 也也就是说
      //   1. value.then 可能报错
      //   2. value.then 可能产生副作用(例如屡次执行可能后果不同)
        var then = value.then

        // 另一方面, 因为无奈保障 then 的确会像预期的那样只调用一个 onFullfilled / onRejected
        // 所以减少了一个 flag 来避免 resolveOrReject 被屡次调用
        var thenAlreadyCalledOrThrow = false
        if (typeof then === 'function') {
        // 是 thenable 那么尝试开展
        // 并且在该 thenable 状态扭转之前 this 对象的状态不变
          then.bind(value)(
          // onFullfilled
            function (value2) {if (thenAlreadyCalledOrThrow) return
              thenAlreadyCalledOrThrow = true
              tryToResolve.bind(this, value2)()}.bind(this),

            // onRejected
            function (reason2) {if (thenAlreadyCalledOrThrow) return
              thenAlreadyCalledOrThrow = true
              resolveOrReject.bind(this, 'rejected', reason2)()}.bind(this)
          )
        } else {
        // 领有 then 然而 then 不是一个函数 所以也不是 thenable
          resolveOrReject.bind(this, 'resolved', value)()}
      } catch (e) {if (thenAlreadyCalledOrThrow) return
        thenAlreadyCalledOrThrow = true
        resolveOrReject.bind(this, 'rejected', e)()}
    } else {
    // 根本类型 间接返回
      resolveOrReject.bind(this, 'resolved', value)()}
  }

  function resolveOrReject (status, data) {if (this.status !== 'pending') return
    this.status = status
    this.data = data
    if (status === 'resolved') {for (var i = 0; i < this.resolveList.length; ++i) {this.resolveList[i]()}
    } else {for (i = 0; i < this.rejectList.length; ++i) {this.rejectList[i]()}
    }
  }

  function Promise (executor) {if (!(this instanceof Promise)) {throw Error('Promise can not be called without new !')
    }

    if (typeof executor !== 'function') {
    // 非标准 但与 Chrome 谷歌保持一致
      throw TypeError('Promise resolver' + executor + 'is not a function')
    }

    this.status = 'pending'
    this.resolveList = []
    this.rejectList = []

    try {executor(tryToResolve.bind(this), resolveOrReject.bind(this, 'rejected'))
    } catch (e) {resolveOrReject.bind(this, 'rejected', e)()}
  }

  Promise.prototype.then = function (onFullfilled, onRejected) {
  // 返回值穿透以及谬误穿透, 留神谬误穿透用的是 throw 而不是 return,否则的话
  // 这个 then 返回的 promise 状态将变成 resolved 即接下来的 then 中的 onFullfilled
  // 会被调用, 然而咱们想要调用的是 onRejected
    if (typeof onFullfilled !== 'function') {onFullfilled = function (data) {return data}
    }
    if (typeof onRejected !== 'function') {onRejected = function (reason) {throw reason}
    }

    var executor = function (resolve, reject) {setTimeout(function () {
        try {
        // 拿到对应的 handle 函数解决 this.data
        // 并以此为根据解析这个新的 Promise
          var value = this.status === 'resolved'
            ? onFullfilled(this.data)
            : onRejected(this.data)
          resolve(value)
        } catch (e) {reject(e)
        }
      }.bind(this))
    }

    // then 承受两个函数返回一个新的 Promise
    // then 本身的执行永远异步与 onFullfilled/onRejected 的执行
    if (this.status !== 'pending') {return new Promise(executor.bind(this))
    } else {
    // pending
      return new Promise(function (resolve, reject) {this.resolveList.push(executor.bind(this, resolve, reject))
        this.rejectList.push(executor.bind(this, resolve, reject))
      }.bind(this))
    }
  }

  // for prmise A+ test
  Promise.deferred = Promise.defer = function () {var dfd = {}
    dfd.promise = new Promise(function (resolve, reject) {
      dfd.resolve = resolve
      dfd.reject = reject
    })
    return dfd
  }

  // for prmise A+ test
  if (typeof module !== 'undefined') {module.exports = Promise}

  return Promise
})()

PromisePolyfill.all = function (promises) {return new Promise((resolve, reject) => {const result = []
    let cnt = 0
    for (let i = 0; i < promises.length; ++i) {promises[i].then(value => {
        cnt++
        result[i] = value
        if (cnt === promises.length) resolve(result)
      }, reject)
    }
  })
}

PromisePolyfill.race = function (promises) {return new Promise((resolve, reject) => {for (let i = 0; i < promises.length; ++i) {promises[i].then(resolve, reject)
    }
  })
}

手写 instanceof 办法

instanceof 运算符用于判断构造函数的 prototype 属性是否呈现在对象的原型链中的任何地位。

实现步骤:

  1. 首先获取类型的原型
  2. 而后取得对象的原型
  3. 而后始终循环判断对象的原型是否等于类型的原型,直到对象原型为 null,因为原型链最终为 null

具体实现:

function myInstanceof(left, right) {let proto = Object.getPrototypeOf(left), // 获取对象的原型
      prototype = right.prototype; // 获取构造函数的 prototype 对象

  // 判断构造函数的 prototype 对象是否在对象的原型链上
  while (true) {if (!proto) return false;
    if (proto === prototype) return true;

    proto = Object.getPrototypeOf(proto);
  }
}

Object.is

Object.is解决的次要是这两个问题:

+0 === -0  // true
NaN === NaN // false
const is= (x, y) => {if (x === y) {
    // + 0 和 - 0 应该不相等
    return x !== 0 || y !== 0 || 1/x === 1/y;
  } else {return x !== x && y !== y;}
}

实现千位分隔符

// 保留三位小数
parseToMoney(1234.56); // return '1,234.56'
parseToMoney(123456789); // return '123,456,789'
parseToMoney(1087654.321); // return '1,087,654.321'
function parseToMoney(num) {num = parseFloat(num.toFixed(3));
  let [integer, decimal] = String.prototype.split.call(num, '.');
  integer = integer.replace(/\d(?=(\d{3})+$)/g, '$&,');
  return integer + '.' + (decimal ? decimal : '');
}

原生实现

function ajax() {let xhr = new XMLHttpRequest() // 实例化,以调用办法
  xhr.open('get', 'https://www.google.com')  // 参数 2,url。参数三:异步
  xhr.onreadystatechange = () => {  // 每当 readyState 属性扭转时,就会调用该函数。if (xhr.readyState === 4) {  //XMLHttpRequest 代理以后所处状态。if (xhr.status >= 200 && xhr.status < 300) {  //200-300 申请胜利
        let string = request.responseText
        //JSON.parse() 办法用来解析 JSON 字符串,结构由字符串形容的 JavaScript 值或对象
        let object = JSON.parse(string)
      }
    }
  }
  request.send() // 用于理论收回 HTTP 申请。不带参数为 GET 申请}

实现 Promise 相干办法

实现 Promise 的 resolve

实现 resolve 静态方法有三个要点:

  • 传参为一个 Promise, 则间接返回它。
  • 传参为一个 thenable 对象,返回的 Promise 会追随这个对象,采纳它的最终状态作为本人的状态。
  • 其余状况,间接返回以该值为胜利状态的 promise 对象。
Promise.resolve = (param) => {if(param instanceof Promise) return param;
  return new Promise((resolve, reject) => {if(param && param.then && typeof param.then === 'function') {
      // param 状态变为胜利会调用 resolve,将新 Promise 的状态变为胜利,反之亦然
      param.then(resolve, reject);
    }else {resolve(param);
    }
  })
}

实现 Promise.reject

Promise.reject 中传入的参数会作为一个 reason 一成不变地往下传, 实现如下:

Promise.reject = function (reason) {return new Promise((resolve, reject) => {reject(reason);
    });
}

实现 Promise.prototype.finally

后面的 promise 不论胜利还是失败,都会走到 finally 中,并且 finally 之后,还能够持续 then(阐明它还是一个 then 办法是要害),并且会将初始的promise 值一成不变的传递给前面的then.

Promise.prototype.finally 最大的作用

  • finally里的函数,无论如何都会执行,并会把后面的值一成不变传递给下一个 then 办法中
  • 如果 finally 函数中有 promise 等异步工作,会等它们全副执行结束,再联合之前的胜利与否状态,返回值

Promise.prototype.finally 六大状况用法

// 状况 1
Promise.resolve(123).finally((data) => { // 这里传入的函数,无论如何都会执行
  console.log(data); // undefined
})

// 状况 2 (这里,finally 办法相当于做了两头解决,起一个过渡的作用)
Promise.resolve(123).finally((data) => {console.log(data); // undefined
}).then(data => {console.log(data); // 123
})

// 状况 3 (这里只有 reject,都会走到下一个 then 的 err 中)
Promise.reject(123).finally((data) => {console.log(data); // undefined
}).then(data => {console.log(data);
}, err => {console.log(err, 'err'); // 123 err
})

// 状况 4 (一开始就胜利之后,会期待 finally 里的 promise 执行结束后,再把后面的 data 传递到下一个 then 中)
Promise.resolve(123).finally((data) => {console.log(data); // undefined
  return new Promise((resolve, reject) => {setTimeout(() => {resolve('ok');
    }, 3000)
  })
}).then(data => {console.log(data, 'success'); // 123 success
}, err => {console.log(err, 'err');
})

// 状况 5 (尽管一开始胜利,然而只有 finally 函数中的 promise 失败了,就会把其失败的值传递到下一个 then 的 err 中)
Promise.resolve(123).finally((data) => {console.log(data); // undefined
  return new Promise((resolve, reject) => {setTimeout(() => {reject('rejected');
    }, 3000)
  })
}).then(data => {console.log(data, 'success');
}, err => {console.log(err, 'err'); // rejected err
})

// 状况 6 (尽管一开始失败,然而也要等 finally 中的 promise 执行完,能力把一开始的 err 传递到 err 的回调中)
Promise.reject(123).finally((data) => {console.log(data); // undefined
  return new Promise((resolve, reject) => {setTimeout(() => {resolve('resolve');
    }, 3000)
  })
}).then(data => {console.log(data, 'success');
}, err => {console.log(err, 'err'); // 123 err
})

源码实现

Promise.prototype.finally = function (callback) {return this.then((data) => {
    // 让函数执行 外部会调用办法,如果办法是 promise,须要期待它实现
    // 如果以后 promise 执行时失败了,会把 err 传递到,err 的回调函数中
    return Promise.resolve(callback()).then(() => data); // data 上一个 promise 的胜利态
  }, err => {return Promise.resolve(callback()).then(() => {throw err; // 把之前的失败的 err,抛出去});
  })
}

实现 Promise.all

对于 all 办法而言,须要实现上面的外围性能:

  • 传入参数为一个空的可迭代对象,则间接进行resolve
  • 如果参数中有一个 promise 失败,那么 Promise.all 返回的 promise 对象失败。
  • 在任何状况下,Promise.all 返回的 promise 的实现状态的后果都是一个数组
Promise.all = function(promises) {return new Promise((resolve, reject) => {let result = [];
    let index = 0;
    let len = promises.length;
    if(len === 0) {resolve(result);
      return;
    }

    for(let i = 0; i < len; i++) {// 为什么不间接 promise[i].then, 因为 promise[i]可能不是一个 promise
      Promise.resolve(promise[i]).then(data => {result[i] = data;
        index++;
        if(index === len) resolve(result);
      }).catch(err => {reject(err);
      })
    }
  })
}

实现 promise.allsettle

MDN: Promise.allSettled()办法返回一个在所有给定的 promise曾经 fulfilledrejected后的promise,并带有一个对象数组,每个对象示意对应的promise` 后果

当您有多个彼此不依赖的异步工作胜利实现时,或者您总是想晓得每个 promise 的后果时,通常应用它。

【译】Promise.allSettledPromise.all 相似, 其参数承受一个 Promise 的数组, 返回一个新的 Promise, 惟一的不同在于, 其不会进行短路, 也就是说当 Promise 全副解决实现后咱们能够拿到每个Promise 的状态, 而不论其是否解决胜利。

用法 | 测试用例

let fs = require('fs').promises;

let getName = fs.readFile('./name.txt', 'utf8'); // 读取文件胜利
let getAge = fs.readFile('./age.txt', 'utf8');

Promise.allSettled([1, getName, getAge, 2]).then(data => {console.log(data);
});
// 输入后果
/*
    [{ status: 'fulfilled', value: 1},
    {status: 'fulfilled', value: 'zf'},
    {status: 'fulfilled', value: '11'},
    {status: 'fulfilled', value: 2}
    ]
*/

let getName = fs.readFile('./name123.txt', 'utf8'); // 读取文件失败
let getAge = fs.readFile('./age.txt', 'utf8');
// 输入后果
/*
    [{ status: 'fulfilled', value: 1},
    {
      status: 'rejected',
      value: [Error: ENOENT: no such file or directory, open './name123.txt'] {
        errno: -2,
        code: 'ENOENT',
        syscall: 'open',
        path: './name123.txt'
      }
    },
    {status: 'fulfilled', value: '11'},
    {status: 'fulfilled', value: 2}
  ]
*/

实现

function isPromise (val) {return typeof val.then === 'function'; // (123).then => undefined
}

Promise.allSettled = function(promises) {return new Promise((resolve, reject) => {let arr = [];
    let times = 0;
    const setData = (index, data) => {arr[index] = data;
      if (++times === promises.length) {resolve(arr);
      }
      console.log('times', times)
    }

    for (let i = 0; i < promises.length; i++) {let current = promises[i];
      if (isPromise(current)) {current.then((data) => {setData(i, { status: 'fulfilled', value: data});
        }, err => {setData(i, { status: 'rejected', value: err})
        })
      } else {setData(i, { status: 'fulfilled', value: current})
      }
    }
  })
}

实现 Promise.race

race 的实现相比之下就简略一些,只有有一个 promise 执行完,间接 resolve 并进行执行

Promise.race = function(promises) {return new Promise((resolve, reject) => {
    let len = promises.length;
    if(len === 0) return;
    for(let i = 0; i < len; i++) {Promise.resolve(promise[i]).then(data => {resolve(data);
        return;
      }).catch(err => {reject(err);
        return;
      })
    }
  })
}

实现一个简版 Promise

// 应用
var promise = new Promise((resolve,reject) => {if (操作胜利) {resolve(value)
    } else {reject(error)
    }
})
promise.then(function (value) {// success},function (value) {// failure})
function myPromise(constructor) {
    let self = this;
    self.status = "pending"   // 定义状态扭转前的初始状态
    self.value = undefined;   // 定义状态为 resolved 的时候的状态
    self.reason = undefined;  // 定义状态为 rejected 的时候的状态
    function resolve(value) {if(self.status === "pending") {
          self.value = value;
          self.status = "resolved";
       }
    }
    function reject(reason) {if(self.status === "pending") {
          self.reason = reason;
          self.status = "rejected";
       }
    }
    // 捕捉结构异样
    try {constructor(resolve,reject);
    } catch(e) {reject(e);
    }
}
// 增加 then 办法
myPromise.prototype.then = function(onFullfilled,onRejected) {
   let self = this;
   switch(self.status) {
      case "resolved":
        onFullfilled(self.value);
        break;
      case "rejected":
        onRejected(self.reason);
        break;
      default:       
   }
}

var p = new myPromise(function(resolve,reject) {resolve(1)
});
p.then(function(x) {console.log(x) // 1
})

应用 class 实现

class MyPromise {constructor(fn) {this.resolvedCallbacks = [];
    this.rejectedCallbacks = [];

    this.state = 'PENDING';
    this.value = '';

    fn(this.resolve.bind(this), this.reject.bind(this));

  }

  resolve(value) {if (this.state === 'PENDING') {
      this.state = 'RESOLVED';
      this.value = value;

      this.resolvedCallbacks.map(cb => cb(value));   
    }
  }

  reject(value) {if (this.state === 'PENDING') {
      this.state = 'REJECTED';
      this.value = value;

      this.rejectedCallbacks.map(cb => cb(value));
    }
  }

  then(onFulfilled, onRejected) {if (this.state === 'PENDING') {this.resolvedCallbacks.push(onFulfilled);
      this.rejectedCallbacks.push(onRejected);

    }

    if (this.state === 'RESOLVED') {onFulfilled(this.value);
    }

    if (this.state === 'REJECTED') {onRejected(this.value);
    }
  }
}

Promise 实现 - 具体

  • 能够把 Promise 看成一个状态机。初始是 pending 状态,能够通过函数 resolvereject,将状态转变为 resolved或者 rejected 状态,状态一旦扭转就不能再次变动。
  • then 函数会返回一个 Promise 实例,并且该返回值是一个新的实例而不是之前的实例。因为 Promise 标准规定除了 pending 状态,其余状态是不能够扭转的,如果返回的是一个雷同实例的话,多个 then 调用就失去意义了。
  • 对于 then来说,实质上能够把它看成是 flatMap
// 三种状态
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";
// promise 接管一个函数参数,该函数会立刻执行
function MyPromise(fn) {
  let _this = this;
  _this.currentState = PENDING;
  _this.value = undefined;
  // 用于保留 then 中的回调,只有当 promise
  // 状态为 pending 时才会缓存,并且每个实例至少缓存一个
  _this.resolvedCallbacks = [];
  _this.rejectedCallbacks = [];

  _this.resolve = function (value) {if (value instanceof MyPromise) {
      // 如果 value 是个 Promise,递归执行
      return value.then(_this.resolve, _this.reject)
    }
    setTimeout(() => { // 异步执行,保障执行程序
      if (_this.currentState === PENDING) {
        _this.currentState = RESOLVED;
        _this.value = value;
        _this.resolvedCallbacks.forEach(cb => cb());
      }
    })
  };

  _this.reject = function (reason) {setTimeout(() => { // 异步执行,保障执行程序
      if (_this.currentState === PENDING) {
        _this.currentState = REJECTED;
        _this.value = reason;
        _this.rejectedCallbacks.forEach(cb => cb());
      }
    })
  }
  // 用于解决以下问题
  // new Promise(() => throw Error('error))
  try {fn(_this.resolve, _this.reject);
  } catch (e) {_this.reject(e);
  }
}

MyPromise.prototype.then = function (onResolved, onRejected) {
  var self = this;
  // 标准 2.2.7,then 必须返回一个新的 promise
  var promise2;
  // 标准 2.2.onResolved 和 onRejected 都为可选参数
  // 如果类型不是函数须要疏忽,同时也实现了透传
  // Promise.resolve(4).then().then((value) => console.log(value))
  onResolved = typeof onResolved === 'function' ? onResolved : v => v;
  onRejected = typeof onRejected === 'function' ? onRejected : r => throw r;

  if (self.currentState === RESOLVED) {return (promise2 = new MyPromise(function (resolve, reject) {
      // 标准 2.2.4,保障 onFulfilled,onRjected 异步执行
      // 所以用了 setTimeout 包裹下
      setTimeout(function () {
        try {var x = onResolved(self.value);
          resolutionProcedure(promise2, x, resolve, reject);
        } catch (reason) {reject(reason);
        }
      });
    }));
  }

  if (self.currentState === REJECTED) {return (promise2 = new MyPromise(function (resolve, reject) {setTimeout(function () {
        // 异步执行 onRejected
        try {var x = onRejected(self.value);
          resolutionProcedure(promise2, x, resolve, reject);
        } catch (reason) {reject(reason);
        }
      });
    }));
  }

  if (self.currentState === PENDING) {return (promise2 = new MyPromise(function (resolve, reject) {self.resolvedCallbacks.push(function () {
        // 思考到可能会有报错,所以应用 try/catch 包裹
        try {var x = onResolved(self.value);
          resolutionProcedure(promise2, x, resolve, reject);
        } catch (r) {reject(r);
        }
      });

      self.rejectedCallbacks.push(function () {
        try {var x = onRejected(self.value);
          resolutionProcedure(promise2, x, resolve, reject);
        } catch (r) {reject(r);
        }
      });
    }));
  }
};
// 标准 2.3
function resolutionProcedure(promise2, x, resolve, reject) {
  // 标准 2.3.1,x 不能和 promise2 雷同,防止循环援用
  if (promise2 === x) {return reject(new TypeError("Error"));
  }
  // 标准 2.3.2
  // 如果 x 为 Promise,状态为 pending 须要持续期待否则执行
  if (x instanceof MyPromise) {if (x.currentState === PENDING) {x.then(function (value) {
        // 再次调用该函数是为了确认 x resolve 的
        // 参数是什么类型,如果是根本类型就再次 resolve
        // 把值传给下个 then
        resolutionProcedure(promise2, value, resolve, reject);
      }, reject);
    } else {x.then(resolve, reject);
    }
    return;
  }
  // 标准 2.3.3.3.3
  // reject 或者 resolve 其中一个执行过得话,疏忽其余的
  let called = false;
  // 标准 2.3.3,判断 x 是否为对象或者函数
  if (x !== null && (typeof x === "object" || typeof x === "function")) {
    // 标准 2.3.3.2,如果不能取出 then,就 reject
    try {
      // 标准 2.3.3.1
      let then = x.then;
      // 如果 then 是函数,调用 x.then
      if (typeof then === "function") {
        // 标准 2.3.3.3
        then.call(
          x,
          y => {if (called) return;
            called = true;
            // 标准 2.3.3.3.1
            resolutionProcedure(promise2, y, resolve, reject);
          },
          e => {if (called) return;
            called = true;
            reject(e);
          }
        );
      } else {
        // 标准 2.3.3.4
        resolve(x);
      }
    } catch (e) {if (called) return;
      called = true;
      reject(e);
    }
  } else {
    // 标准 2.3.4,x 为根本类型
    resolve(x);
  }
}

实现 Promisify

const fs = require('fs')
const path = require('path')

// node 中应用
// const fs = require('fs').promises 12.18 版
// const promisify = require('util').promisify

// 包装 node api promise 化 典型的高级函数
const promisify = fn=>{return (...args)=>{return new Promise((resolve,reject)=>{fn(...args, (err,data)=>{if(err) {reject(err)
        } 
        resolve(data)
      })
    })
  }
}

// const read = promisify(fs.readFile)

// read(path.join(__dirname, './promise.js'), 'utf8').then(d=>{//   console.log(d)
// })

// promise 化 node 所有 api
const promisifyAll = target=>{Reflect.ownKeys(target).forEach(key=>{if(typeof target[key] === 'function') {target[key+'Async'] = promisify(target[key])
    }
  })
  return target
}

// promise 化 fs 下的函数
const promisifyNew = promisifyAll(fs)

promisifyNew.readFileAsync(path.join(__dirname, './promise.js'), 'utf8').then(d=>{console.log(d)
})

module.exports = {
  promisify,
  promisifyAll
}

残缺实现 Promises/A+ 标准

/**
 * Promises/A+ 标准 实现一个 promise
 * https://promisesaplus.com/
*/

const EMUM = {
  PENDING: 'PENDING',
  FULFILLED: 'FULFILLED',
  REJECTED: 'REJECTED'
}

// x 返回值
// promise2 then 的时候 new 的 promise
// promise2 的 resolve, reject
const resolvePromise = (x, promise2, resolve, reject)=>{
  // 解析 promise 的值解析 promise2 是胜利还是失败 传递到上层 then
  if(x === promise2) {reject(new TypeError('类型谬误'))
  }
  // 这里的 x 如果是一个 promise 的话 可能是其余的 promise,可能调用了胜利 又调用了失败
  // 避免 resolve 的时候 又 throw err 抛出异样到 reject 了
  let called
  // 如果 x 是 promise 那么就采纳他的状态
  // 有 then 办法是 promise
  if(typeof x === 'object' && typeof x!== null || typeof x === 'function') {
    // x 是对象或函数
    try {
      let then = x.then // 缓存,不必屡次取值
      if(typeof then === 'function') {
        // 是 promise,调用 then 办法外面有 this,须要传入 this 为 x 能力取到 then 办法外面的值 this.value
        then.call(x, y=>{// 胜利
          // y 值可能也是一个 promise 如 resolve(new Promise()) 此时的 y ==new Promise()
          // 递归解析 y,直到拿到一般的值 resolve(x 进来)
          if(called) return;
          called = true;

          resolvePromise(y, promise2, resolve, reject)
        },r=>{// 一旦失败间接失败
          if(called) return;
          called = true;
          reject(r)
        })
      } else {
        // 一般对象不是 promise
        resolve(x)
      }
    } catch (e) {
      // 对象取值可能报错,用 defineProperty 定义 get 抛出异样
      if(called) return;
      called = true;
      reject(e)
    }
  } else {
    // x 是一般值
    resolve(x) // 间接胜利
  }

}
class myPromise {constructor(executor) {
    this.status = EMUM.PENDING // 以后状态
    this.value = undefined // resolve 接管值
    this.reason = undefined // reject 失败返回值

    /**
     * 同一个 promise 能够 then 屡次(公布订阅模式)
     * 调用 then 时 以后状态是期待态,须要将以后胜利或失败的回调寄存起来(订阅)* 调用 resolve 时 将订阅函数进行执行(公布)*/
    // 胜利队列
    this.onResolvedCallbacks = []
    // 失败队列
    this.onRejectedCallbacks = []
    const resolve = value =>{
      // 如果 value 是一个 promise,须要递归解析
      // 如 myPromise.resolve(new myPromise()) 须要解析 value
      if(value instanceof myPromise) {
        // 不停的解析 直到值不是 promise
        return value.then(resolve,reject)
      }

      if(this.status === EMUM.PENDING) {
        this.status = EMUM.FULFILLED
        this.value = value

        this.onResolvedCallbacks.forEach(fn=>fn())
      }
    }
    const reject = reason =>{if(this.status === EMUM.PENDING) {
        this.status = EMUM.REJECTED
        this.reason = reason

        this.onRejectedCallbacks.forEach(fn=>fn())
      }
    }
    try {executor(resolve,reject)
    } catch(e) {reject(e)
    }
  }
  then(onFulFilled, onRejected) {
    // 透传 解决默认不传的状况
    // new Promise((resolve,reject)=>{//   resolve(1)
    // }).then().then().then(d=>{})
    // new Promise((resolve,reject)=>{//   resolve(1)
    // }).then(v=>v).then(v=>v).then(d=>{})
    // new Promise((resolve,reject)=>{//   reject(1)
    // }).then().then().then(null, e=>{console.log(e)})
    // new Promise((resolve,reject)=>{//   reject(1)
    // }).then(null,e=>{throw e}).then(null,e=>{throw e}).then(null,e=>{console.log(e)})
    onFulFilled = typeof onFulFilled === 'function' ? onFulFilled : v => v
    onRejected = typeof onRejected === 'function' ? onRejected : err => {throw err}

    // 调用 then 创立一个新的 promise
    let promise2 = new myPromise((resolve,reject)=>{
      // 依据 value 判断是 resolve 还是 reject value 也可能是 promise
      if(this.status === EMUM.FULFILLED) {setTimeout(() => {
          try {
            // 胜利回调后果
            let x = onFulFilled(this.value)
            // 解析 promise
            resolvePromise(x, promise2,resolve,reject)
          } catch (error) {reject(error)
          }
        }, 0);
      }
      if(this.status === EMUM.REJECTED) {setTimeout(() => {
          try {let x = onRejected(this.reason)
            // 解析 promise
            resolvePromise(x, promise2,resolve,reject)
          } catch (error) {reject(error)
          }
        }, 0);
      }
      // 用户还未调用 resolve 或 reject 办法
      if(this.status === EMUM.PENDING) {this.onResolvedCallbacks.push(()=>{
          try {let x = onFulFilled(this.value)
            // 解析 promise
            resolvePromise(x, promise2,resolve,reject)
          } catch (error) {reject(error)
          }
        })
        this.onRejectedCallbacks.push(()=>{
          try {let x = onRejected(this.reason)
            // 解析 promise
            resolvePromise(x, promise2,resolve,reject)
          } catch (error) {reject(error)
          }
        })
      }
    })

    return promise2
  }
  catch(errCallback) {
    // 等同于没有胜利,把失败放进去而已
    return this.then(null, errCallback)
  }
  // myPromise.resolve 具备期待性能的 如果参数的 promise 会期待 promise 解析结束在向下执行
  static resolve(val) {return new myPromise((resolve,reject)=>{resolve(val)
    })
  }
  // myPromise.reject 间接将值返回
  static reject(reason) {return new myPromise((resolve,reject)=>{reject(reason)
    })
  }
  // finally 传入的函数 无论胜利或失败都执行
  // Promise.reject(100).finally(()=>{console.log(1)}).then(d=>console.log('success',d)).catch(er=>console.log('faild',er))
  // Promise.reject(100).finally(()=>new Promise()).then(d=>console.log(d)).catch(er=>)
  finally(callback) {return this.then((val)=>{return myPromise.resolve(callback()).then(()=>val)
    },(err)=>{return myPromise.resolve(callback()).then(()=>{throw err})
    })
  }
  // Promise.all
  static all(values) {return new myPromise((resolve,reject)=>{let resultArr = []
      let orderIndex = 0
      const processResultByKey = (value,index)=>{resultArr[index] = value 
        // 解决齐全部
        if(++orderIndex === values.length) {resolve(resultArr) // 解决实现的后果返回去
        }
      }
      for (let i = 0; i < values.length; i++) {const value = values[i];
        // 是 promise
        if(value && typeof value.then === 'function') {value.then((val)=>{processResultByKey(val,i)
          },reject)
        } else {
          // 不是 promise 状况
          processResultByKey(value,i)
        }
      }
    })
  }
  static race(promises) {
    // 采纳最新胜利或失败的作为后果
    return new myPromise((resolve,reject)=>{for (let i = 0; i < promises.length; i++) {let val = promises[i]
        if(val && typeof val.then === 'function') {
          // 任何一个 promise 先调用 resolve 或 reject 就返回后果了 也就是返回执行最快的那个 promise 的后果
          val.then(resolve,reject)
        }else{
          // 一般值
          resolve(val)
        }
      }
    })
  }
}


/**
 * ===== 测试用例 -====
 */
// let promise1 = new myPromise((resolve,reject)=>{//   setTimeout(() => {//     resolve('胜利')
//   }, 900);
// })

// promise1.then(val=>{//   console.log('success', val)
// },reason=>{//   console.log('fail', reason)
// })

/**
 * then 的应用形式 一般值象征不是 promise
 * 
 * 1、then 中的回调有两个办法 胜利或失败 他们的后果返回(一般值)会传递给外层的下一个 then 中
 * 2、能够在胜利或失败中抛出异样,走到下一次 then 的失败中
 * 3、返回的是一个 promsie,那么会用这个 promise 的状态作为后果,会用 promise 的后果向下传递
 * 4、错误处理,会默认先找离本人最新的错误处理,找不到就向下查找,找打了就执行
 */

// read('./name.txt').then(data=>{
//   return '123'
// }).then(data=>{//}).then(null,err=>{//})
// // .catch(err=>{ // catch 就是没有胜利的 promise

// // })

/**
 * promise.then 实现原理:通过每次返回一个新的 promise 来实现(promise 一旦胜利就不能失败,失败就不能胜利)* 
 */

// function read(data) {//   return new myPromise((resolve,reject)=>{//     setTimeout(() => {//       resolve(new myPromise((resolve,reject)=>resolve(data)))
//     }, 1000);
//   })
// }

// let promise2 = read({name: 'poetry'}).then(data=>{
//   return data
// }).then().then().then(data=>{//   console.log(data,'-data-')
// },(err)=>{//   console.log(err,'-err-')
// })

// finally 测试
// myPromise
//   .resolve(100)
//   .finally(()=>{//     return new myPromise((resolve,reject)=>setTimeout(() => {//       resolve(100)
//     }, 100))
//   })
//   .then(d=>console.log('finally success',d))
//   .catch(er=>console.log(er, 'finally err'))


/**
 * promise.all 测试
 * 
 * myPromise.all 解决并发问题 多个异步并发获取最终的后果
*/

// myPromise.all([1,2,3,4,new myPromise((resolve,reject)=>{//   setTimeout(() => {//     resolve('ok1')
//   }, 1000);
// }),new myPromise((resolve,reject)=>{//   setTimeout(() => {//     resolve('ok2')
//   }, 1000);
// })]).then(d=>{//   console.log(d,'myPromise.all.resolve')
// }).catch(err=>{//   console.log(err,'myPromise.all.reject')
// })


// 实现 promise 中断请求
let promise = new Promise((resolve,reject)=>{setTimeout(() => {
    // 模仿接口调用 ajax 调用超时
    resolve('胜利') 
  }, 10000);
})

function promiseWrap(promise) {
  // 包装一个 promise 能够管制原来的 promise 是胜利 还是失败
  let abort
  let newPromsie = new myPromise((resolve,reject)=>{abort = reject})
  // 只有管制 newPromsie 失败,就能够管制被包装的 promise 走向失败
  // Promise.race 任何一个先胜利或者失败 就能够取得后果
  let p = myPromise.race([promise, newPromsie])
  p.abort = abort

  return p
}

let newPromise = promiseWrap(promise)

setTimeout(() => {
  // 超过 3 秒超时
  newPromise.abort('申请超时')
}, 3000);

newPromise.then(d=>{console.log('d',d)
}).catch(err=>{console.log('err',err)
})


// 应用 promises-aplus-tests 测试写的 promise 是否标准
// 全局装置 cnpm i -g promises-aplus-tests
// 命令行执行 promises-aplus-tests promise.js
// 测试入口 产生提早对象
myPromise.defer = myPromise.deferred = function () {let dfd = {}
  dfd.promise = new myPromise((resolve,reject)=>{
    dfd.resolve = resolve
    dfd.reject = reject
  })
  return dfd
}

// 提早对象用户
// ![](http://img-repo.poetries.top/images/20210509172817.png)
// promise 解决嵌套问题
// function readData(url) {//   let dfd = myPromise.defer()
//   fs.readFile(url, 'utf8', function (err,data) {//     if(err) {//       dfd.reject()
//     }
//     dfd.resolve(data)
//   })
//   return dfd.promise
// }
// readData().then(d=>{
//   return d
// })

module.exports = myPromise

Array.prototype.forEach()

Array.prototype.forEach = function(callback, thisArg) {if (this == null) {throw new TypeError('this is null or not defined');
  }
  if (typeof callback !== "function") {throw new TypeError(callback + 'is not a function');
  }
  const O = Object(this);
  const len = O.length >>> 0;
  let k = 0;
  while (k < len) {if (k in O) {callback.call(thisArg, O[k], k, O);
    }
    k++;
  }
}

instanceof

instanceof运算符用于检测构造函数的 prototype 属性是否呈现在某个实例对象的原型链上。

const myInstanceof = (left, right) => {
  // 根本数据类型都返回 false
  if (typeof left !== 'object' || left === null) return false;
  let proto = Object.getPrototypeOf(left);
  while (true) {if (proto === null) return false;
    if (proto === right.prototype) return true;
    proto = Object.getPrototypeOf(proto);
  }
}

实现一个 JS 函数柯里化

事后解决的思维,利用闭包的机制

  • 柯里化的定义:接管一部分参数,返回一个函数接管残余参数,接管足够参数后,执行原函数
  • 函数柯里化的次要作用和特点就是 参数复用 提前返回 提早执行
  • 柯里化把屡次传入的参数合并,柯里化是一个高阶函数
  • 每次都返回一个新函数
  • 每次入参都是一个

当柯里化函数接管到足够参数后,就会执行原函数,如何去确定何时达到足够的参数呢?

有两种思路:

  • 通过函数的 length 属性,获取函数的形参个数,形参的个数就是所需的参数个数
  • 在调用柯里化工具函数时,手动指定所需的参数个数

将这两点联合一下,实现一个简略 curry 函数

通用版

// 写法 1
function curry(fn, args) {
  var length = fn.length;
  var args = args || [];
  return function(){newArgs = args.concat(Array.prototype.slice.call(arguments));
      if (newArgs.length < length) {return curry.call(this,fn,newArgs);
      }else{return fn.apply(this,newArgs);
      }
  }
}
// 写法 2
// 分批传入参数
// redux 源码的 compose 也是用了相似柯里化的操作
const curry = (fn, arr = []) => {// arr 就是咱们要收集每次调用时传入的参数
  let len = fn.length; // 函数的长度,就是参数的个数

  return function(...args) {let newArgs = [...arr, ...args] // 收集每次传入的参数

    // 如果传入的参数个数等于咱们指定的函数参数个数,就执行指定的真正函数
    if(newArgs.length === len) {return fn(...newArgs)
    } else {
      // 递归收集参数
      return curry(fn, newArgs)
    }
  }
}
// 测试
function multiFn(a, b, c) {return a * b * c;}

var multi = curry(multiFn);

multi(2)(3)(4);
multi(2,3,4);
multi(2)(3,4);
multi(2,3)(4)

ES6 写法

const curry = (fn, arr = []) => (...args) => (
  arg => arg.length === fn.length
    ? fn(...arg)
    : curry(fn, arg)
)([...arr, ...args])
// 测试
let curryTest=curry((a,b,c,d)=>a+b+c+d)
curryTest(1,2,3)(4) // 返回 10
curryTest(1,2)(4)(3) // 返回 10
curryTest(1,2)(3,4) // 返回 10
// 柯里化求值
// 指定的函数
function sum(a,b,c,d,e) {return a + b + c + d + e}

// 传入指定的函数,执行一次
let newSum = curry(sum)

// 柯里化 每次入参都是一个参数
newSum(1)(2)(3)(4)(5)

// 偏函数
newSum(1)(2)(3,4,5)
// 柯里化简略利用
// 判断类型,参数多少个,就执行多少次收集
function isType(type, val) {return Object.prototype.toString.call(val) === `[object ${type}]`
}

let newType = curry(isType)

// 相当于把函数参数一个个传了,把第一次先缓存起来
let isString = newType('String')
let isNumber = newType('Number')

isString('hello world')
isNumber(999)

实现一个 JSON.stringify

JSON.stringify(value[, replacer [, space]]):
  • Boolean | Number| String类型会主动转换成对应的原始值。
  • undefined、任意函数以及symbol,会被疏忽(呈现在非数组对象的属性值中时),或者被转换成 null(呈现在数组中时)。
  • 不可枚举的属性会被疏忽如果一个对象的属性值通过某种间接的形式指回该对象自身,即循环援用,属性也会被疏忽
  • 如果一个对象的属性值通过某种间接的形式指回该对象自身,即循环援用,属性也会被疏忽
function jsonStringify(obj) {
    let type = typeof obj;
    if (type !== "object") {if (/string|undefined|function/.test(type)) {obj = '"'+ obj +'"';}
        return String(obj);
    } else {let json = []
        let arr = Array.isArray(obj)
        for (let k in obj) {let v = obj[k];
            let type = typeof v;
            if (/string|undefined|function/.test(type)) {v = '"'+ v +'"';} else if (type === "object") {v = jsonStringify(v);
            }
            json.push((arr ? "":'"' + k + '":') + String(v));
        }
        return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}")
    }
}
jsonStringify({x : 5}) // "{"x":5}"
jsonStringify([1, "false", false]) // "[1,"false",false]"
jsonStringify({b: undefined}) // "{"b":"undefined"}"

实现 ES6 的 extends

function B(name){this.name = name;};
function A(name,age){
  //1. 将 A 的原型指向 B
  Object.setPrototypeOf(A,B);
  //2. 用 A 的实例作为 this 调用 B, 失去继承 B 之后的实例,这一步相当于调用 super
  Object.getPrototypeOf(A).call(this, name)
  //3. 将 A 原有的属性增加到新实例上
  this.age = age; 
  //4. 返回新实例对象
  return this;
};
var a = new A('poetry',22);
console.log(a);

实现双向数据绑定

let obj = {}
let input = document.getElementById('input')
let span = document.getElementById('span')
// 数据劫持
Object.defineProperty(obj, 'text', {
  configurable: true,
  enumerable: true,
  get() {console.log('获取数据了')
  },
  set(newVal) {console.log('数据更新了')
    input.value = newVal
    span.innerHTML = newVal
  }
})
// 输出监听
input.addEventListener('keyup', function(e) {obj.text = e.target.value})

实现深拷贝

  • 浅拷贝: 浅拷贝指的是将一个对象的属性值复制到另一个对象,如果有的属性的值为援用类型的话,那么会将这个援用的地址复制给对象,因而两个对象会有同一个援用类型的援用。浅拷贝能够应用 Object.assign 和开展运算符来实现。
  • 深拷贝: 深拷贝绝对浅拷贝而言,如果遇到属性值为援用类型的时候,它新建一个援用类型并将对应的值复制给它,因而对象取得的一个新的援用类型而不是一个原有类型的援用。深拷贝对于一些对象能够应用 JSON 的两个函数来实现,然而因为 JSON 的对象格局比 js 的对象格局更加严格,所以如果属性值里边呈现函数或者 Symbol 类型的值时,会转换失败

(1)JSON.stringify()

  • JSON.parse(JSON.stringify(obj))是目前比拟罕用的深拷贝办法之一,它的原理就是利用 JSON.stringifyjs 对象序列化(JSON 字符串),再应用 JSON.parse 来反序列化 (还原)js 对象。
  • 这个办法能够简略粗犷的实现深拷贝,然而还存在问题,拷贝的对象中如果有函数,undefined,symbol,当应用过 JSON.stringify() 进行解决之后,都会隐没。
let obj1 = {  a: 0,
              b: {c: 0}
            };
let obj2 = JSON.parse(JSON.stringify(obj1));
obj1.a = 1;
obj1.b.c = 1;
console.log(obj1); // {a: 1, b: {c: 1}}
console.log(obj2); // {a: 0, b: {c: 0}}

(2)函数库 lodash 的_.cloneDeep 办法

该函数库也有提供_.cloneDeep 用来做 Deep Copy

var _ = require('lodash');
var obj1 = {
    a: 1,
    b: {f: { g: 1} },
    c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);// false

(3)手写实现深拷贝函数

// 深拷贝的实现
function deepCopy(object) {if (!object || typeof object !== "object") return;

  let newObject = Array.isArray(object) ? [] : {};

  for (let key in object) {if (object.hasOwnProperty(key)) {newObject[key] =
        typeof object[key] === "object" ? deepCopy(object[key]) : object[key];
    }
  }

  return newObject;
}

正文完
 0