call、aplly、bind 实现 && this理解

发布时间:2022-05-06 发布网站:前端之家 F2er.com
前端之家收集整理的这篇文章主要介绍了call、aplly、bind 实现 && this理解 前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

本质

call、aplly、bind 本质都是改变 this 的指向,不同点 call、aplly 是直接调用函数,bind 是返回一个新的函数。call 跟 aplly 就只有参数上不同。

手写实现

bind 实现

  • 箭头函数的 this 永远指向它所在的作用域
  • 在方法调用模式下,this 总是指向调用它所在方法的对象;this 的指向与所在方法的调用位置有关,而与方法的声明位置无关(箭头函数特殊)
  • 函数作为构造函数用 new 关键字调用时,不应该改变其 this 指向,因为 new绑定 的优先级高于 显示绑定 和 硬绑定
// bind 实现
Function.prototype.djBind = function (thisArg) {
  if (typeof this !== "function") {
    throw TypeError("Bind must be called on function");
  }

  // 拿到参数args,为了传给调用者
  const args = Array.prototype.slice.call(arguments,1),self = this,nop = function () {},bound = function () {
      // this instanceof nop,判断是否使用 new 来调用 bound
      // 如果是 new 来调用的话,this的指向就是其实例,
      // 如果不是 new 调用的话,就改变 this 指向到指定的对象 o
      return self.apply(
        this instanceof nop ? this : thisArg,args.concat(Array.prototype.slice.call(arguments))
      );
    };

  // 箭头函数没有 prototype,箭头函数this永远指向它所在的作用域
  if (this.prototype) {
      nop.prototype = this.prototype;
  } 
  // 修改绑定函数的原型指向
  bound.prototype = new nop();

  return bound;
};

Test bind

const bar = function() {
  console.log(this.name,arguments);
};
 
bar.prototype.name = 'bar';
 
const foo = {
  name: 'foo'
};
 
const bound = bar.djBind(foo,22,33,44);
new bound(); // bar,[22,44]    this指向bar 
bound(); // foo,44

call 实现

Function.prototype.djCall = function(thisArg) {
    if(typeof this !== 'function') {
        throw TypeError("call must be called on function");
    }

    const args = Array.prototype.slice.call(arguments,1);
    thisArg = thisArg || window;
    
    // 将调用call函数的对象添加到thisArg的属性中 
    thisArg.fn = this;
    // 执行该属性,在thisArg上调用this方法;
    const result = thisArg.fn(...args);

    delete thisArg.fn;
    return result;
}

问题

  • fn 同名覆盖问题
    可以使用Symbol,解决同名冲突问题;
  • ...展开操作符问题

apply 实现

Function.prototype.djApply = function(thisArg) {
    if(typeof this !== 'function') {
        throw TypeError("call must be called on function");
    }

    const args = arguments[1];
    thisArg = thisArg || window;
    
    // 将调用call函数的对象添加到thisArg的属性中 
    thisArg.fn = this;
    // 执行该属性
    const result = thisArg.fn(...args);

    delete thisArg.fn;
    return result;
}

Test call && apply


const bar = function() {
  console.log(this.name,arguments);
};
 
bar.prototype.name = 'bar';
 
const foo = {
  name: 'foo'
};
 
bar.mycall(foo,1,2,3);   // foo [1,3]
bar.myaplly(foo,[1,3]);   // foo [1,3]

this

JS中的this是一个非常简单的东西,只需要理解它的执行规则;
call/apply/bind可以显式绑定,这里就不说了;如上所述;
主要这些场隐式绑定的场景讨论:

  • 全局上下文
  • 直接调用函数
  • 对象.方法的形式调用
  • DOM事件绑定(特殊)
  • new构造函数绑定
  • 箭头函数
  1. 全局上下文
    全局上下文默认this指向window,严格模式下指向undefined。
  2. 直接调用函数
    比如:
let obj = {
  a: function() {
    console.log(this);
  }
}
let func = obj.a;
func();

这种情况是直接调用。this相当于全局上下文的情况。

  1. 对象.方法的形式调用
    还是刚刚的例子,我如果这样写:
    obj.a();

这就是对象.方法的情况,this指向这个对象

  1. DOM事件绑定
    onclick和addEventerListener中 this 默认指向绑定事件的元素。
    IE比较奇异,使用attachEvent,里面的this默认指向window。
  2. new+构造函数
    此时构造函数中的this指向实例对象。
  3. 箭头函数?
    箭头函数没有this,因此也不能绑定。里面的this会指向当前最近的非箭头函数的this,找不到就是window(严格模式是undefined)。比如:
let obj = {
  a: function() {
    let do = () => {
      console.log(this);
    }
    do();
  }
}
obj.a(); // 找到最近的非箭头函数a,a现在绑定着obj,因此箭头函数中的this是obj

优先级: new > call、apply、bind > 对象.方法 > 直接调用。

总结


以上是前端之家为你收集整理的call、aplly、bind 实现 && this理解 全部内容,希望文章能够帮你解决call、aplly、bind 实现 && this理解 所遇到的程序开发问题。

如果觉得前端之家网站内容还不错,欢迎将前端之家网站推荐给前端开发程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。