原理
要使this绑定到目标对象,我们可以利用this的隐式绑定这一特性,想办法让目标对象成为函数调用时的上下文对象。我们以Array.prototype.forEach为例,来对如何改变this绑定进行说明。
// forEach调用时的上下文对象为arr
// 所以this会绑定到arr
const arr = ["h", "e", "l", "l", "o"];
arr.forEach();
// 我们将Array的forEach方法
// 给到String.prototype上
String.prototype.forEach = Array.prototype.forEach
// 这样一来,String也有自己的forEach方法了
const str = String("hello"),
// 当str调用forEach时,上下文对象为str
// 所以this绑定成功绑定到了str
str.forEach()
这亦是函数call、apply和bind的实现原理,接下来我们如上代码进行一定的封装。
Function.prototype.call
// Function.prototype.myCall
// @params { Any } obj: this绑定到的目标对象
// @params { Any } ...args: 参数
Function.prototype.myCall = function (obj, ...args) {
// 如果无上下文对象,则应用默认绑定
obj = obj || window;
// 如果obj为基本类型,先将其包装成对象
obj = Object(obj);
// 避免func与obj原有属性发生命名冲突
const func = Symbol();
// 将this,即function给到obj上
obj[func] = this;
// 调用funcion,此时的上下文对象是obj,this也绑定在obj上
const result = obj[func](...args);
// 用完记得删除[func]这一属性
delete obj[func];
// 返回结果
return result;
};
// 测试
const str = "hello";
Array.prototype.forEach.myCall(str, function (val) {
console.log(val);
});
Function.prototype.apply
// Function.prototype.myApply
// @params { Any } obj: this绑定到的目标对象
// @params { Array } args: 参数
Function.prototype.myApply = function (obj, args) {
obj = obj || window;
obj = Object(obj);
const func = Symbol();
obj[func] = this;
const result = obj[func](...args);
delete obj[func];
return result;
};
// 测试
const str = "hello";
Array.prototype.forEach.myApply(str, [
function (val) {
console.log(val);
},
]);
Function.prototype.bind
// Function.prototype.myBind
// @params { Any } obj: this绑定到的目标对象
// @params { Any } ...args: 参数
Function.prototype.myBind = function (obj, ...args) {
obj = obj || window;
obj = Object(obj);
const func = Symbol();
obj[func] = this;
// 支持函数柯里化
return function (...newArgs) {
obj[func](...args, ...newArgs);
};
};
// 测试
const str = "hello";
const strForEach = Array.prototype.forEach.myBind(str);
strForEach(function (val) {
console.log(val);
});