call()
和apply()
的区别在于,call()
方法接受的是若干个参数的列表,而apply()
方法接受的是一个包含多个参数的数组。
举例:
var func = function(arg1, arg2) {
...
};
func.call(this, arg1, arg2); // 使用 call,参数列表
func.apply(this, [arg1, arg2]) // 使用 apply,参数数组
一、call的模拟实现
我们先来看一个简单的例子。
var value = 1
var foo = {
value: 4
}
function bar(){
console.log(this.value)
}
bar.call(foo) //4
通过上面代码实现,我们可以看出以下两点:
call()
改变了this指向- 函数
bar
执行了
call实现代码
Function.prototype.call_ = function (obj) {
obj = obj ? Object(obj) : window;
obj.fn = this;
// 利用拓展运算符直接将arguments转为数组
let args = [...arguments].slice(1);
let result = obj.fn(...args);
delete obj.fn
return result;
};
二、apply的模拟实现
Function.prototype.apply_ = function (obj, arr) {
obj = obj ? Object(obj) : window;
obj.fn = this;
let result;
if (!arr) {
result = obj.fn();
} else {
result = obj.fn(...arr);
};
delete obj.fn
return result;
};
三、bind的模拟实现
Function.prototype.bind2 = function (context) {
if (typeof this !== "function") {
throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}
var self = this;
var args = Array.prototype.slice.call(arguments, 1); //使用arguments获取参数数组并作为 self.apply()的第二个参数
var fNOP = function () {}; //创建一个空对象
var fBound = function () {
var bindArgs = Array.prototype.slice.call(arguments); //此时的arguments指的是bind返回的函数传入的参数 获取返回参数的数组,并与之前的合并
return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs)); //使用apply绑定this
// 当作为构造函数使用时,this指向实例,将绑定函数的this指向该实例,可以让实例获得来自绑定函数的值
// 当作为普通函数使用时,this指向window,将绑定函数的this指向context
}
// 修改返回函数的prototype为绑定函数的prototype,实例就可以继承绑定函数原型中的值
fNOP.prototype = this.prototype; //空对象的原型指向绑定函数的原型
fBound.prototype = new fNOP(); //空对象的实例赋值给fBound.prototype
return fBound; //使用return返回一个函数
}