深入理解call、apply、bind,并手写代码
分类: 前端开发
2024-05-26
浏览: 116
评论: 0
字数: 35478

前言

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

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

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

call, apply, bind 的异同

相同点

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

不同点

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

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

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

手写call, apply, bind

手写apply

javascript
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

javascript
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

javascript
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);
点赞
打赏
本文标签:
本文链接:
版权声明:
本文由vscing原创发布,转载请遵循《署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)》许可协议授权