前端八股文-手写new、bind、call、apply的三两事

1,051 阅读3分钟

系列文章

前端八股文-类型判断

前端八股文-原型链

前端八股文-继承

new本质

说明:new操作符实际返回的是一个对象-实例对象

原理:

  • 1、创建一个空对象
  • 2、将对象的__proto__指向函数的原型对象
  • 3、改变this指向,将this指向生成的对象
  • 4、返回生成的对象

代码实现:

function copyNew(Fn) {
   return function () {
    // 创建一个新对象且将其隐式原型指向构造函数原型
    let obj = {
      __proto__ : Fn.prototype
    }
    // 执行构造函数
    Fn.call(obj, ...arguments)
    // 返回该对象
    return obj
  }
}

call

说明:本质是将函数放到对象中,然后调用对象中的该函数,改变this的指向

原理:

  • 1、将原函数存放到传入的上下午对象中
  • 2、获取原函数传入的所有参数
  • 3、上下文中存放的原函数,将参数传入并调用
  • 4、将上下午中临时存放的原函数删除
  • 5、返回调用原函数返回的结果

代码实现:

Function.prototype.newCall = function(context) {
   if (typeof this !== 'function') {
    throw new Error('not function')
   } 
   // 若没有上下文,指向windows
   context = context || windows;
   // 将当前函数存放到上下文
   context.fn = this;
   // 获取所有函数参数  es5写法 Array.prototype.slice.call(arguments, 1);
   let args = [...arguments].slice(1);
   let result = context.fn(...arg);
   delete context.fn
   return result;       
}

apply

说明:本质与call一致,区别在于参数以数组方式传入

原理:

  • 1、将原函数存放到传入的上下午对象中
  • 2、获取原函数传入的所有参数
  • 3、判断是否存在参数,调用上下文中存放的原函数,若存在参数,结构传入
  • 4、将上下午中临时存放的原函数删除
  • 5、返回调用原函数返回的结果

代码实现:

Function.prototype.newApply = function(context) {
   if (typeof this !== 'function') {
    throw new Error('not function')
   } 
   // 若没有上下文,指向windows
   context = context || windows;
   // 将当前函数存放到上下文
   context.fn = this;
   let result;
   // 判断是否存在参数组,若严谨一点,需要判断传入的是否是数组
   if(arguments[1]) {
       result = context.fn(...arguments[1]);
   } else {
       result = context.fn();
   }
   delete context.fn
   return result;       
}

bind

说明:本质是返回一个改变了this指向的原函数

定义:

  • bind方法会创建一个新函数。当这个新函数被调用时,bind的第一个参数将作为它运行时的this(该参数不能被重写), 之后的一序列参数将会在传递的实参前传入作为它的参数。
  • 新函数也能使用new操作符创建对象:这种行为就像把原函数当成构造器, 提供的this值被忽略。

原理:

  • 1、将当前函数存放一份副本
  • 2、获取所有传入的参数
  • 3、返回一个新函数
  • 4、新函数判断是否是使用了new的情况
  • 5、非new情况,使用apply改变this指向,传入所有参数
  • 6、new的情况,返回一个原函数的实例
  • 或 使用一个中转函数继承原函数的原型,将中转函数的实例赋值给返回的函数的原型,返回函数调用时,this通过instanceof 判断中转函数的原型是否在其原型链上,则返回原函数,传入所有参数即可。外面再使用new关键字。结果同实现一

代码实现一:

Function.prototype.newBind = function(context) {
   if (typeof this !== 'function') {
    throw new Error('not function')
   } 
   const _self = this;
   const args = [...arguments];
   return function F() {
        if(this instanceof F) {
            return new _self(...args, ...arguments)
        }  else {
             return _self.apply(context||windows, [...args,...arguments])
        }
   }
}

代码实现二:

 Function.prototype.newBind1 = function (context) {
      let self = this;
      let args = Array.prototype.slice.call(arguments, 1);
      const cacheFn = function () {};
      const newFn = function () {
        const newArgs = [...args, ...arguments];
        if (this instanceof cacheFn) {
          return self.apply(this, newArgs)
        } else {
          return self.apply(context, newArgs)
        }
      };
      cacheFn.prototype = self.prototype;
      newFn.prototype = new cacheFn(); // 继承原函数的原型
      return newFn
}

instanceof

说明:原型链查找

实现:

function instanceof(left, right) {
  let leftValue = left.__proto__
  let rightValue = right.prototype
  while (true) {
    if (leftValue === null) {
      return false
    }
    if (leftValue === rightValue) {
      return true
    }
    leftValue = leftValue.__proto__
  }
}