Skip to content

apply、call、bind 实现

在 JavaScript 中我们经常会使用 apply、call、bind 等方法进行函数的调用或者 this 的绑定。
那么它们内部是如何实现的呢?我们可以通过自己实现这些方法来了解它们内部的原理和机制。
接下来我们会实现属于自己的这些方法:hycall、hyapply、hybind。

一. call 方法实现

1.1. hycall 的实现代码

这是一个 JavaScript 函数的扩展方法,它被称为hycall,通过调用该方法可以使得函数被执行,并且可以指定函数中的this上下文对象。

下面是这个函数的实现过程。

javascript
// 给所有的函数添加一个hycall的方法
Function.prototype.hycall = function (thisArg, ...args) {
  // 在这里可以去执行调用的那个函数(foo)
  // 问题: 得可以获取到是哪一个函数执行了hycall
  // 1.获取需要被执行的函数
  var fn = this;

  // 2.对thisArg转成对象类型(防止它传入的是非对象类型)
  thisArg =
    thisArg !== null && thisArg !== undefined ? Object(thisArg) : window;

  // 3.调用需要被执行的函数
  thisArg.fn = fn;
  var result = thisArg.fn(...args);
  delete thisArg.fn;

  // 4.将最终的结果返回出去
  return result;
};

上述代码中:

  • Function.prototype用来给所有的函数对象添加一个扩展方法 hycall,它的作用是改变函数的上下文对象,使得函数的执行上下文对象可以被指定为 thisArg。

  • 函数的参数可以通过args参数列表传递给被调用函数。

1.2. hycall 的测试代码

接下来我们可以编写一下测试代码:

javascript
function foo() {
  console.log("foo函数被执行", this);
}

function sum(num1, num2) {
  console.log("sum函数被执行", this, num1, num2);
  return num1 + num2;
}

// 系统的函数的call方法
foo.call(undefined);
var result = sum.call({}, 20, 30);
// console.log("系统调用的结果:", result)

// 自己实现的函数的hycall方法
// 默认进行隐式绑定
// foo.hycall({name: "why"})
foo.hycall(undefined);
var result = sum.hycall("abc", 20, 30);
console.log("hycall的调用:", result);

二. apply 方法实现

2.1. hyapply 的实现代码

这是一个 JavaScript 函数的扩展方法,它被称为hyapply,通过调用该方法可以使得函数被执行,并且可以指定函数中的this上下文对象以及函数的参数。

下面是这个函数的实现过程。

javascript
// 自己实现 hyapply
Function.prototype.hyapply = function (thisArg, argArray) {
  // 1.获取到要执行的函数
  var fn = this;

  // 2.处理绑定的 thisArg
  thisArg =
    thisArg !== null && thisArg !== undefined ? Object(thisArg) : window;

  // 3.执行函数
  thisArg.fn = fn;
  var result;

  // argArray = argArray ? argArray: []
  argArray = argArray || [];
  result = thisArg.fn(...argArray);

  delete thisArg.fn;

  // 4.返回结果
  return result;
};

上述代码中:

  • Function.prototype 用来给所有的函数对象添加一个扩展方法 hyapply,它的作用是改变函数的上下文对象,使得函数的执行上下文对象可以被指定为 thisArg,并且可以将参数列表传递给被调用函数。

2.2. hyapply 的测试代码

javascript
function sum(num1, num2) {
  console.log("sum 被调用", this, num1, num2);
  return num1 + num2;
}

function foo(num) {
  return num;
}

function bar() {
  console.log("bar 函数被执行", this);
}

// 系统调用
// var result = sum.apply("abc", 20)
// console.log(result)

// 自己实现的调用
var result = sum.hyapply("abc", [20, 30]);
console.log(result);

var result2 = foo.hyapply("abc", [20]);
console.log(result2);

三. bind 方法实现

2.1. hybind 的实现代码

这是一个 JavaScript 函数的扩展方法,它被称为 hybind,通过调用该方法可以使得函数的上下文对象被永久绑定,并且可以指定函数的参数。

下面是这个函数的实现过程:

javascript
Function.prototype.hybind = function (thisArg, ...argArray) {
  // 1.获取到真实需要调用的函数
  var fn = this;

  // 2.绑定 this
  thisArg =
    thisArg !== null && thisArg !== undefined ? Object(thisArg) : window;

  function proxyFn(...args) {
    // 3.将函数放到 thisArg 中进行调用
    thisArg.fn = fn;
    // 特殊: 对两个传入的参数进行合并
    var finalArgs = [...argArray, ...args];
    var result = thisArg.fn(...finalArgs);
    delete thisArg.fn;

    // 4.返回结果
    return result;
  }

  return proxyFn;
};

上述代码中:

  • Function.prototype用来给所有的函数对象添加一个扩展方法 hybind,它的作用是改变函数的上下文对象,使得函数的执行上下文对象可以被永久绑定,并且可以将参数列表传递给被调用函数。

2.2. hybind 的测试代码

javascript
function foo() {
  console.log("foo 被执行", this);
  return 20;
}

function sum(num1, num2, num3, num4) {
  console.log(num1, num2, num3, num4);
}

// 系统的 bind 使用
var bar = foo.bind("abc");
bar();

// var newSum = sum.bind("aaa", 10, 20, 30, 40)
// newSum()

// var newSum = sum.bind("aaa")
// newSum(10, 20, 30, 40)

// var newSum = sum.bind("aaa", 10)
// newSum(20, 30, 40)

// 使用自己定义的 bind
// var bar = foo.hybind("abc")
// var result = bar()
// console.log(result)

var newSum = sum.hybind("abc", 10, 20);
var result = newSum(30, 40);