一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第8天,点击查看活动详情。
前言
之前写过一篇关于this指向的问题,其中提到了可以通过call(),apply(),bind()三种方法改变this的指向,有兴趣的可以点击去看看,这篇文章来写一下call(),apply(),bind()的实现。
call()
首先要明确call的参数是什么,都是什么意义。
- call的第一个参数是将来调用者this的指向
- 剩下的参数作为实参传递给调用者
要实现第一条我们只要把第一参数设置为对象的属性就行了,那么代码就可以这样实现
Function.prototype.myCall = function(context){
context.fn = this;
context.fn();
delete context.fn;
}
这里可以测试一下,
animal={color:'yellow'}
function dog(){
console.log(this.color)
}
dog.myCall(animal) //这里的打印结果就会是yellow,这就实现了call的第一个功能,改变this的指向
接着来实现第二部分,也就是把剩余的参数当做实参传递给函数执行者
在这里我们要知道一个知识点,那就是arguments可以去到所有的实参,这里我们模仿call的特性,取出其中的第二个到最后一个,然后放到数组中。
animal={color:'yellow'}
function dog(age,name){
console.log(arguments)
console.log(this.color)
console.log(age)
console.log(name)
}
dog(6,'小白')
上面代码打印的arguments如下图:
既然已经拿到了所有的参数,那我们就可以实现第二部分内容了,最终代码如下
Function.prototype.myCall = function(context){
context.fn = this;
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
eval('context.fn(' + args +')');
delete context.fn;
}
在试一下,如下图
可以看到,this的指向也改变了,参数也都传递成功了,这里已经差不多可以算是成功了,但是还有一个钟情况就是this可能传递的是null,也就是说不传递参数的时候,那这时候其实就是使用的window,这时候就要对代码进行最后一步的调整了,如下
Function.prototype.myCall = function(context){
var context = context || window;
context.fn = this; //通过this来获取调用myCall的函数
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
eval('context.fn(' + args +')');
delete context.fn;
}
这时候就很完美了
上述代码中的push实参和把剩余参数从数组变成参数的方法也可以写成下面这种方式
Function.prototype.myCall = function(context){
context = context || window
context.fn= this
var args = []
for(var i=1;i<arguments.length;i++){
args.push(arguments[i])
}
var result = context.fn(...args)
delete context.fn
return result
}
总结
call()的实现大概就是这样,完结,撒花