手动实现call、bind、apply

call、bind、apply

作用

改变函数执行上下文,即函数运行时this的指向。

实质

就是重新给函数绑定参数。

区别

call、apply的区别:接受参数的方式不一样。

  • call 第一个参数是 this 的指向,从第二个参数开始是接收的参数列表

  • apply 第一个参数是要绑定给 this 的值,第二个参数是一个参数数组。当第一个参数为 null、undefined的时候,默认指向window.

  • 都是立即执行

bind和call 类似。 第一个参数是 this 的指向,从第二个参数开始是接收的参数列表。区别在于 bind 方法返回值是函数以及 bind 接收的参数列表的使用。

模拟实现call

  • 判断当前this是否为函数,防止Function.prototype.myCall() 直接调用
  • context 为可选参数,如果不传的话默认上下文为 window
  • 为context 创建一个 Symbol(保证不会重名)属性,将当前函数赋值给这个属性
  • 处理参数,传入第一个参数后的其余参数
  • 调用函数后即删除该Symbol属性
Function.prototype.myCall = function(content = window, ...args) {
   if(this === Function.prototype) return undefined; // 判断当前this是否为函数,防止Function.prototype.myCall() 直接调用
   if(typeof this !== 'function') throw new TypeError('The this must be a Function'); // 必须为函数
   const fn = Symbol();
   content[fn] = this;
   const result = content[fn](...args);
   delete context[fn]
   return result;
}
复制代码

模拟实现apply

Function.prototype.myApply = function(content = window, args) {
  if(this === Function.prototype) return undefined; // 判断当前this是否为函数,防止Function.prototype.myCall() 直接调用
  if(typeof this !== 'function') throw new TypeError('The `this` must be a `Function`'); // 必须为函数
  const fn = Symbol();
  content[fn] = this;
  const result = Array.isArray(args) ? content[fn](...args) : content[fn](); // 参数为数组
  delete content[fn]
  return result;
}
复制代码

模拟实现bind

  • 处理参数,返回一个闭包
  • 返回的新函数也可以使用new操作符,所以在新函数内部需要判断是否使用了new操作符, 判断是否为构造函数调用,如果是 则使用new调用当前函数
  • 如果不是,使用apply,将context和处理好的参数传入

Function.prototype.myBind = function(content = window, ...args) {
  if(this === Function.prototype) return undefined; // 判断当前this是否为函数,防止Function.prototype.myCall() 直接调用
  if(typeof this !== 'function') throw new TypeError('The `this` must be a `Function`'); // 必须为函数
  const fn = this;
  return function F(...otherArgs) {
      // 判断是否用于构造函数
      if(this instanceof F) return new fn(...args, ...otherArgs);
     return fn.apply(content, [...args, ...otherArgs])
  }
}

//简洁版的new操作符实现过程
function new(constructor) {
   var obj = {}; //第一步:创建一个空对象obj 
   obj.__proto__ = constructor.prototype; //第二步:将构造函数 constructor的原型对象赋给obj的原型
   contructor.apply(obj);//第三步:将构造函数 constructor中的this指向obj,并立即执行构造函数内部的操作
   return obj; // 第四步:返回这个对象
 }
 
 有人用下面的笨方法,不过可以加深了解这么做的原因。
 
 Function.prototype.myBind2 = function(content = window, ...args) {
   const fn = this;
   // 如果不进行判断是否使用了new,就会导致 " 将构造函数 constructor中的this指向obj " 这一过程失效
   const F = function(...otherArgs) {
     return fn.apply(this instanceof T ? this : content, [...args, ...otherArgs])
   } 
   //创建一个中转函数T,让F间接继承目标函数的原型
   const T = function(){}
   T.prototype = fn.prototype;
   F.prototype = new T(); // 最后这里new一下。-_- 返回
   // F.prototype= new T()将F.prototype的__poro__指向T.prototype,然后T.prototype = that.prototype,所以此时改变F.prototype并不会影响fn.prototype
   return F;
 }

复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享