this、call()、apply()、bind()

1.this 的理解

this 是执行上下文中的一个属性,它指向最后一次调用这个方法的对象。在开发中,this 的指向可以通过 4 中调用模式来判断。

  • 1.1.函数调用模式:
    当一个函数不是一个对象的属性时候,直接作为一个函数来调用,this 指向全局对象。
  • 1.2.方法调用模式:
    当一个函数是有一个对象来调用时,this 指向这个对象。
  • 1.3.构造器调用模式:
    当一个函数用 new 调用时,函数执行前会去创建一个对象,this 指向这个新创建的对象。
  • 1.4.apply、bind、call 调用模式:
    this 指向方法中的第一个参数

2.call、apply、bind

这三个方法都可以改变 this 的指向。

三者的区别

  • call()、apply()方法是立即调用当前函数,而 bind()是返回一个改变了 this 指向的新函数,并不立即调用。
  • call()传递的参数形式是:call(newThis,参数 1,参数 2...),传递参数数量不固定,
    apply()传递的参数形式是:apply(newThis,[参数 1,参数 2,...]),接收 2 个参数,第一个参数是函数体内 this 指向,第二
    各参数是一个数组。
  • bind()可以像 call()一样传参,但是由于它返回的是一个函数,也可以在返回的函数调用时传参。

2.1.call()

使用方法: fun.call(newThis,参数 1,参数 2...)

手写 call 函数步骤

  1. 考虑写在哪里,应该卸载构造函数的原型对象上
  2. 考虑传入那些参数,原生 call 传入上下文对象和参数,且参数是一个一个传递
  3. 考虑传入的上下文对象是原始类型还是引用类型,如果传入的是原始类型,其中上下文指向包装类型的对象,
    比如传入数字 1,this 指向[Number: 1],传入 null 和 undefined,this 指向全局对象,传入引用引用类型,则
    指向该引用类型自身。所以传入的上下文对象可以使用 Object()构造函数将传入的值转换成一个对象
    (原始类型中比如 number、string、boolean 会转换成包装类对象,引用类型为自身)。
  4. 想知道谁在调用 myCall()方法, 就需要知道 this,this 就是调用者,因此需要给上下文对象加一个 this 属性,
    但是不可以随便定义属性名,可以使用 Symbol(),创建的值是唯一的。同时也让这个属性不可枚举。
  5. 调用该方法,并接收一个返回值
  6. 调用完成之后,删除这个属性
  7. 返回函数调用的返回值
Function.prototype.myCall = function (context, ...args) {
  context = context == null || context == undefined ? globalThis : Object(context);
  const key = Symbol('this');
  Object.defineProperty(context, key, {
    value: this,
    enumerable: false,
  });
  const result = context[key](...args);
  delete context[key];
  return result;
};

function method(a, b) {
  console.log(a, b); // 1 2
  console.log(this); // { name: '张三' }
}

method.myCall({ name: '张三' }, 1, 2);

2.2.apply()

使用方法: fun.apply(newThis,[参数 1,参数 2...])
手写 apply 函数步骤

apply 和 call 的区别在于传参,所以更改传参即可, 给第二个入参给一个默认值 [ ]

Function.prototype.myApply = function (context, args = []) {
  context = context == null || context == undefined ? globalThis : Object(context);
  const key = Symbol('this');
  Object.defineProperty(context, key, {
    value: this,
    enumerable: false,
  });
  const result = context[key](...args);
  delete context[key];
  return result;
};

function method(a, b) {
  console.log(a, b);
  console.log(this);
}

method.myApply({ name: '张三' }, [1]);

2.1.bind()

使用方法: let newFun = fun.bind(newThis,参数 1,参数 2...); newFun(参数 3,参数 4...)
手写 bind 函数步骤

其大体步骤和手动实现 call 和 apply 类似,但是 bind 是返回一个新的函数,所以要返回一个新的函数,这个函数内部调用了调用者自身的方法,也要注意传入参数的时机和个数。

Function.prototype.myBind = function (context, ...args1) {
  return (...args2) => {
    context = context == null || context == undefined ? globalThis : Object(context);
    const key = Symbol('this');
    Object.defineProperty(context, key, {
      value: this,
      enumerable: false,
    });
    const result = context[key](...args1, ...args2);
    delete context[key];
    return result;
  };
};

function method(a, b, c, d) {
  console.log(a, b, c, d); // 1 2 3 4
  console.log(this); // { name: '张三' }
}

let result = method.myBind({ name: '张三' }, 1, 2);
result(3, 4);

posted on 2023-12-30 22:15  前端自信逐梦者  阅读(28)  评论(0)    收藏  举报

导航