apply、call、bind的简单实现

322 阅读3分钟

一、apply、call、bind的基本用法

1-1、apply

语法: func.apply(thisArg, [argsArray])

参数:

  • thisArg
    • 必选,func函数运行时this的指向
  • argsArray
    • 可选,数组/类数组对象。作为参数传递给func函数
var array = ['a', 'b'];
var elements = [0, 1, 2];
array.push.apply(array, elements);
console.info(array); // ["a", "b", 0, 1, 2]

1-2、call

语法: func.call(thisArg, arg1, arg2, ...)

参数:

  • thisArg
    • 必选,func函数运行时this的指向
  • arg1, arg2,...
    • 可选,指定的参数列表
function sum(num1, num2) {
  console.log(this);// {}
  return num1 + num2;
}
var result = sum.call({}, 10, 20);
console.log(result); // 30

1-3、bind

语法: func.bind(thisArg[, arg1[, arg2[, ...]]])

参数:

  • thisArg
    • 必选,func函数运行时this的指向
  • arg1, arg2,...
    • 可选,指定的参数列表

返回值: 返回一个原函数的拷贝,并且拥有指定的this值和初始参数

var obj = {
  name: 'zs'
}
function foo() {
  console.log(this);
}
var bar = foo.bind(obj);
bar(); // {name: 'zs'}

二、apply、call、bind的异同

相同点:

  • 都用于显示绑定this
  • 第一个参数如果传递为null或undefined,则this默认指向window

不同点:

  • 传递参数形式和返回值不同
    • 第一个参数都接收this指向,但apply第二个参数接收的是一个数组或类数组对象
    • call与bind第二个参数接收的是参数列表
    • apply与call调取后立即执行,并将this绑定为指定的内容;而bind不是立即执行,而是返回一个原函数的拷贝

三、apply、call、bind的简单实现

3-1、实现apply

Function.prototype.myApply = function(thisArg, argsArray) {
  // 1 获取要执行的函数
  // fn.apply(xx, xxx)由隐式绑定规则可以得到,this即所要执行的函数
  var fn = this;
  
  // 2 处理一下thisArg,如果传递null或undefined则this指向window;否则包装为对象
  thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window;

  // 3 将执行函数给传递的this(进行隐式绑定)
  thisArg.fn = fn;

  var result;
  argsArray = argsArray || [];
  // 4 执行
  result = thisArg.fn(...argsArray);

  // 5 执行完去删除一下,防止有多余的属性
  delete thisArg.fn;
  
  // 6 返回结果
  return result;
}
  • 执行apply时,是通过 函数.apply(xxx)的方式来执行的,相当于进行了隐式绑定,所以可通过this获取到执行函数
  • 要为传递的this进行判断是否为null和undefined,如果是则this指向window,否则指向传递的对象
    • 注意,要进行Object包裹,一旦传入基本数据类型,要指向其包装类型。如传递数字则this指向Number,传递字符串则this指向String
  • 将执行函数再给thisArg,通过thisArg去发起调用,这样通过隐式绑定的原理,this便指向了发起调用的对象thisArg (this绑定规则详见上一篇文章~)

3-2、实现call

Function.prototype.myCall = function(thisArg, ...args) {
  var fn = this;
  thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window;
  
  thisArg.fn = fn;
  var result = thisArg.fn(...args);
  
  delete thisArg.fn;
  return result;
}

3-3、实现bind

Function.prototype.myBind = function(thisArg, ...args) {
  var fn = this;
  thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg): window;

  function newFn(...args2) {
    thisArg.fn = fn;

    // 合并参数
    var finalArgs = [...args, ...args2];
    var result = thisArg.fn(...finalArgs);

    delete thisArg.fn;
    return result;
  }
  return newFn;
}

// 调用
function sum(num1, num2, num3, num4) {
  console.log(num1, num2, num3, num4)
}
var newSum = sum.myBind("abc", 1,2);
var result = newSum(3,4); // 1,2,3,4
  • 调取bind后返回的新函数也可接收多个参数,会与调取bind传递的参数进行合并