call
Function.prototype.myCall = function (thisArg, ...rest) {
const fn = this;
thisArg = thisArg === null || thisArg === undefined ? window : thisArg;
thisArg = Object(thisArg);
const uniqueKey = Symbol();
thisArg[uniqueKey] = fn;
const result = thisArg[uniqueKey](...rest);
delete thisArg[uniqueKey];
return result;
};
function foo(num1, num2) {
console.log('foo的this:', this);
return `foo的返回值: ${num1 + num2}`;
}
console.log(foo.myCall({ name: 'feng' }, 2, 3));
apply
Function.prototype.myApply = function (thisArg, restArr = []) {
const fn = this;
thisArg = thisArg === null || thisArg === undefined ? window : thisArg;
thisArg = Object(thisArg);
const uniqueKey = Symbol();
thisArg[uniqueKey] = fn;
const result = thisArg[uniqueKey](...restArr);
delete thisArg[uniqueKey];
return result;
};
function foo(num1, num2) {
console.log('foo的this:', this);
return num1 + num2;
}
console.log(foo.myApply(null, [1, 2]));
bind
Function.prototype.myBind = function (thisArg, ...rest) {
const fn = this;
thisArg = thisArg === null || thisArg === undefined ? window : thisArg;
thisArg = Object(thisArg);
return function (...restArgs) {
const uniqueKey = Symbol();
thisArg[uniqueKey] = fn;
const result = thisArg[uniqueKey](...rest, ...restArgs);
delete thisArg[uniqueKey];
return result;
}
};
function foo(num1, num2, num3) {
console.log('foo的this:', this);
return num1 + num2 + num3;
}
const bar = foo.myBind({ name: 'feng' }, 1, 2);
const result = bar(3);
console.log(result);
总结
- 原生call、apply、bind的实现是由C++编写(V8引擎)的,所以使用JS只能尽可能地实现功能。
- 实现思路:使用隐式绑定从而改变this的指向。
- 在使用JS实现的过程中,主体逻辑是比较容易编写的,需要花费精力的地方更多是边界情况的处理。对于函数来说,边界情况通常从参数和返回值这两方面考虑。
- 函数的本质是延迟执行代码。从bind方法可以体现该本质。
- 涉及到的知识
- 原型prototype
- this的指向
- 类型转换(
Object())
- Symbol的用处
- 剩余参数
- 展开运算符
- 默认参数
- 删除对象属性
- 闭包
- 原生call、apply、bind的使用与区别