javascript call方法的用处及原理

6,543 阅读2分钟

call的作用

一句话:改变调用方法的this指向,示例如下:

function a(){
	console.log(this);
}
a();
a.call({name:"西瓜"});

可以看到,第一次this指向了window,第二次this指向了传入的对象;

那么在执行call方法的时候,内部做了什么处理呢?

先来看一个东西,或许就能明白

var a = {
	name:"西瓜",
	run:function(){
		console.log(this);
		console.log(this.name);
	}
}
a.run();

上面这段代码,相信小伙伴都能看懂,其实,call执行的时候,也是做了这样的处理;。

手动来实现一个call

Function.prototype.MyCall = function(obj){
	var newObj = obj || window;
	newObj.fn = this;
	var params = [...arguments].slice(1);
	var result = newObj.fn(...params);
	delete newObj.fn;
	return result;
}

就上面的这段代码,就实现了一个call的功能,来看下这段代码都做了哪些事情

  1. 定义一个新的对象,若传入的obj存在,则新对象等于obj,若obj不存在,则等于window
  2. 把this挂在到当前定义的新对象上(this即为调用的函数);
  3. 第4行代码处理了函数的传参;
  4. 然后执行创建的新对象的fn函数(即为要调用的函数);
  5. 最后在执行了以后,把这个挂载的fn删除;

来验证一下是否正确

function test(){
	console.log(this);
}
test();
test.MyCall({name:"西瓜"});

结果如下图:

可以看到,结果和使用call的时候输出的一致。这样就自己手动实现了一个call方法;

接下来,讲一个我在学习call的时候,网上经常讲到的一个案例,我们一起来看一下:

function f1(a){
	console.log(1);
	console.log(this);
}
function f2(){
	console.log(2);
	console.log(this);
}

f1.call(f2);
f1.call.call(f2);

想一下,这段代码打印出来的会是一个什么结果呢?

f1.call(f2)的打印结果,相信大家都能理解,这里就不解释了;重点来讲一下f1.call.call(f2); 首先,我们用刚刚自己实现的call方法来试试,会不会出现同样的结果;

f1.MyCall(f2);
f1.MyCall.MyCall(f2);

结果当然是一样的啦!如下:

为什么会这样呢,来谈谈我对这个结果的理解吧(可能其他小伙伴有更好的理解)! 把上面自己实现的call方法复制下来

Function.prototype.MyCall = function(obj){
    var newObj = obj || window;
	newObj.fn = this;
	var params = [...arguments].slice(1);
	var result = newObj.fn(...params);
	delete newObj.fn;
	return result;
}

当我们执行f1.MyCall.MyCall(f2)的时候,上面这个MyCall方法中的thisnewObj指的分别是什么? 我的理解是thisf1.MyCall,即为Function.prototype.MyCallnewObjf2方法; 所当f1.MyCall.MyCall(f2)执行的时候,结果就变了以下结果:

var newObj = f2;
f2.fn = Function.prototype.MyCall;

接下来MyCall的第5行是不是会执行newObj.fn? 其实就是执行f2.MyCall()这个结果大家应该都知道了吧;

如果说上面这个理解了,可以想想下面几个会打印出什么内容:

f1.call.call.call(f2)
f1.call.call.call.call(f2)
f1.call.call.call.call.call(f2)

其实都是一样的哈!

结尾

除了call,还有两个类似的方法apply,bind,这里就不做过多的解释了,相信小伙伴一定也能跟着这样的思路去手写出来。有什么问题就给我留言吧!