深入理解call、apply、bind,并手写代码

2024-05-26 23:31:54

浏览:178

评论:0

前言

call() 方法调用一个函数, 其具有一个指定的this值和分别地提供的参数(参数的列表)。

apply() 方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数。

bind() 方法创建一个新的函数,当这个新函数被调用时其this置为提供的值,其参数列表前几项置为创建时指定的参数序列。

call, apply, bind 的异同

相同点

  • 都能改变函数内部this关键字的指向,允许你以灵活的方式控制函数的执行环境。
  • 调用函数时传递额外的参数,尽管传递方式有所不同。

不同点

  • call() 和 apply() 直接调用目标函数,并立即执行。 bind() 不会立即调用函数,而是返回一个新的函数,这个新函数的this值被永久地绑定到了传入的值。
  • call() 接受一个参数列表。apply() 接受两个参数:第一个是上下文,第二个是一个数组或类数组对象,它包含了要传递给函数的所有参数。bind() 可以预先设置一部分参数(这些参数在新函数调用时位于参数列表的开始位置),并且返回的新函数可以在调用时继续接收其他参数。
func.call(context, arg1, arg2, ...);

func.apply(context, [arg1, arg2, ...]);

var newFunc = func.bind(context, arg1, arg2);
// 后续调用
newFunc(arg3, arg4);

手写call, apply, bind

手写apply

Function.prototype._apply = function(context, args = []){
  console.log(this, context, args);
  // 首先判断this指向
  if(typeof this !== 'function'){
    throw new TypeError('this 指向必须是一个函数')
  }
  // 没有context,或者传递的是 null undefined,则重置为window或global
  if(!context) context = window || global;

  // 指定唯一属性,防止 delete 删除错误
  const fn = Symbol();

  // 将 this 添加到 context的属性上
  context[fn] = this;

  // 直接调用context 的 fn,上下文this就指向了context
  const result = context[fn](...args);
  
  // 删除掉context新增的symbol属性
  delete context[fn];

  return result;
}

const obj = {
  name: 'vscing'
}
function getNameArr(...arr) {
  console.log(arr, this.name); // [1, 2, 3, 4, 5] "vscing"
}

getNameArr._apply(obj, [1,2,3,4,5]); 

手写bind

Function.prototype._bind = function(context, ...args){
  console.log(this, context, args);
  const that = this;
  // 首先判断this指向
  if(typeof that !== 'function'){
    throw new TypeError('this 指向必须是一个函数')
  }
  // 没有context,或者传递的是 null undefined,则重置为window或global
  if(!context) context = window || global;

  return function(...param) {
    // 指定唯一属性,防止 delete 删除错误
    const fn = Symbol();

    // 将 this 添加到 context的属性上
    context[fn] = that;

    // 直接调用context 的 fn,上下文this就指向了context
    const result = context[fn](...args, ...param);

    // 删除掉context新增的symbol属性
    delete context[fn]

    return result
  }
}

const obj = {
  name: 'vscing'
}
function getNameArr(...arr) {
  console.log(arr, this.name); // [1, 2, 3, 4, 5] "vscing"
}

getNameArr._bind(obj, 1,2,3)(4, 5); 

手写call

Function.prototype._call = function(context, ...args){
  console.log(this, context, args);
  // 首先判断this指向
  if(typeof this !== 'function'){
    throw new TypeError('this 指向必须是一个函数')
  }
  // 没有context,或者传递的是 null undefined,则重置为window或global
  if(!context) context = window || global;

  // 指定唯一属性,防止 delete 删除错误
  const fn = Symbol();

  // 将 this 添加到 context的属性上
  context[fn] = this;

  // 直接调用context 的 fn,上下文this就指向了context
  const result = context[fn](...args);
  
  // 删除掉context新增的symbol属性
  delete context[fn];

  return result;
}

const obj = {
  name: 'vscing'
}
function getNameArr(...arr) {
  console.log(arr, this.name); // [1, 2, 3, 4, 5] "vscing"
}

getNameArr._call(obj, 1,2,3,4,5);