手写call、apply、bind方法

call方法

call、apply和bind方法我们都知道是用来绑定this的,那么它内部做了什么事情呢?其实也不复杂,对于call来说总结下来也就五步

  1. 在封装的call函数内部需要拿到需要绑定的函数
  2. 然后将传进来的第一个参数,也就是this转换为对象,顺手判断一下是否为undefined或者是null。因为原生call方法传入这两个参数绑定的是window
  3. 然后将这个对象加入一个属性,value就是拿到的函数
  4. 利用隐式绑定调用一下这个函数,调用完就可以删除掉这个函数了
  5. 最后将结果返回出去即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 1. call的实现:
function foo(a, b, c) {
console.log(this.name);
console.log(a + b + c);
}
//利用ES6提供的剩余参数
Function.prototype.weiCall = function (thisArg, ...arg) {
//edge case,判断传入的参数是否合法
thisArg =
thisArg !== null && thisArg !== undefined ? Object(thisArg) : window;
thisArg.fn = this;
const result = thisArg.fn(...arg);
delete thisArg.fn;
return result;
};
foo.weiCall({ name: "coderwei" }, 1, 2, 3); //coderwei,6

因为我们在调用的时候就是通过函数名.weiCall的形式调用的,所以这里会进行隐式绑定,weiCall这个函数内部的this就是我们需要的函数

apply方法

apply和call一样,都是用来绑定this指向的,他们不同点在于后面传递的参数不同,call方法传递的参数是是以逗号分隔的,而apply方法传入的是一个数组。

1
2
3
4
5
6
7
8
9
10
11
12
// 2. apply 函数的实现
Function.prototype.weiApply = function (thisArg, arg) {
thisArg =
thisArg !== null && thisArg !== undefined ? Object(thisArg) : window;

thisArg.fn = this;
const result = thisArg.fn(...arg);
delete thisArg.fn;
return result;
};

foo.weiApply({ name: "coderwei" }, [1, 2, 3]);

所以weiApply方法的第二个参数不需要用剩余参数来接收,因为他就两个参数,一个this的指向,一个数组

但是执行函数的时候我们需要用展开运算符将数组展开作为函数的参数

bind方法

bind方法需要注意一点,因为bind方法返回的是一个函数,所以说他可以在绑定的时候传递两个参数,在执行的执行把剩余一个参数传递进来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 3. bind函数的实现
Function.prototype.weiBind = function (thisArg, ...arg) {
const _this = this;
thisArg =
thisArg !== null && thisArg !== undefined ? Object(thisArg) : window;
// 返回一个函数,依旧是接收不定量的参数
return function (...arg2) {
// 将所有参数拼接到一个数组中
const allArg = [...arg, ...arg2];
thisArg.fn = _this;
// 将所有参数展开挨个传入函数中
const result = thisArg.fn(...allArg);
delete thisArg.fn;
return result;
};
};

const bar = foo.weiBind({ name: "coderwei" }, 1, 2);
bar(3);