call() 方法调用一个函数, 其具有一个指定的this值和分别地提供的参数(参数列表)。
apply() 方法调用一个具有给定this值的函数,以及作为一个数组(或类数组对象)提供的参数。
bind() 方法创建一个新的函数,在调用时设置this关键字为提供的值。并在调用新函数时,将给定参数列表作为原函数的参数序列的前若干项。
以上内容源自MDN
下面我用自己的理解说一下call,apply及bind都做了啥
foo.call(obj,argument1,augument2,argument3)复制代码
以上代码的作用:
执行函数foo,并将其this指定为obj,后面的参数为调用foo时传入的参数
foo.apply(obj,[argument1,augument2,argument3])复制代码
以上代码的作用:
执行函数foo,并将其this指定为obj并将数组
let bar = foo.bind(obj,argument1,augument2,argument3);
bar();复制代码
以上代码的作用:
返回一个绑定了this为obj,参数为argument1,augument2,argument3的函数,所以如果想要执行foo,需要把返回结果再执行一下
了解了方法的功能和原理,接下来让我们分别实现它们
call的实现:
首先实现一个简单的call方法
Function.prototype.call2 = function(obj){
obj.fn = this;
if(arguments.length>1){
obj.fn(...([...arguments].slice(1)));
}else{
obj.fn();
}
delete obj.fn;
}复制代码
以上代码仅仅实现了this的绑定以及函数调用传参,但是忽略了函数的返回值,而且默认把当前函数赋给了obj.fn,而没有考虑obj.fn存在的情况,改进后的代码如下:
Function.prototype.call2 = function(obj){
let _fn = "fn",result;
while (obj.hasOwnProperty(_fn)) {
_fn = "fn" + Math.random(); // 循环判断并重新赋值
}
obj[_fn] = this;
if(arguments.length>1){
result = obj[_fn](...([...arguments].slice(1)));
}else{
result = obj[_fn]();
}
delete obj[_fn];
return result;
}
复制代码
这样似乎完善了一些,但是还有一种情况需要考虑,即调用call方法没有传参的情况,或者第一个参数为null或者undefined
var name = 'window.name';
function foo(){
console.log(this);
console.log(this.name)
};
foo.call(); // window window.name
foo.call(1); // Number {1} undefined复制代码
通过上面的代码我们可以得出结论,如果调用call方法没有传入参数或者第一个参数为null或者undefined,会将函数的this绑定到window对象(注意如上测试代码,不可以使用let声明name哦),而如果传入的参数为基本数据类型,会将其转换为对象
继续完善后的call方法如下:
Function.prototype.call2 = function(obj){
obj = obj?Object(obj):window;
let _fn = "fn",result;
while (obj.hasOwnProperty(_fn)) {
_fn = "fn" + Math.random(); // 循环判断并重新赋值
}
obj[_fn] = this;
if(arguments.length>1){
result = obj[_fn](...([...arguments].slice(1)));
}else{
result = obj[_fn]();
}
delete obj[_fn];
return result;
}
复制代码
而有了如上思考过程,实现apply就很简单了,代码如下:
Function.prototype.apply2 = function(obj,arr){
obj = obj?Object(obj):window;
let _fn = "fn",result;
while (obj.hasOwnProperty(_fn)) {
_fn = "fn" + Math.random(); // 循环判断并重新赋值
}
obj[_fn] = this;
if(arr){
result = obj[_fn](...arr);
}else{
result = obj[_fn]();
}
delete obj[_fn];
return result;
}
复制代码
bind实现:
Function.prototype.bind2 = function(obj){
obj = obj?Object(obj):window;
var myArguments = arguments,self = this;
if(arguments.length>1){
return function(){
self.apply(obj,[...myArguments].slice(1))
};
}else{
return function(){
self.apply(obj);
};
}
}
复制代码
bind的实现借用了apply方法,有兴趣的朋友可以尝试不借用apply实现bind
如果有错误或者不严谨的地方,请给予指正,十分感谢!