这个系列的教程我一开始是写在github上的,
可是以为放到掘金来可让更多须要的人看到,
就搬到掘金专栏上啦,
若是以为本教程对你有帮助,请点这里去github上给我一颗Star~
教程目录也在github上哈~javascript
下面开始第三篇:java
想当年我仍是个小白的时候,看到call和apply,那都是一脸懵逼啊!
再加上参数内部this,arguments什么的,虐的我不要不要的,一度产生厌学心理。
的确,这俩方法对初学者不够友好...git
可是!做为半个老鸟,如今看到call啊什么apply啊什么的,也就微微一笑了。
想当初茅塞顿开的时候,那内心叫一个痛快,如今就把开窍的过程分享出来。github
先说一下call和apply的区别,你在彻底不懂俩函数是干吗的状况下,你只要记住:
call和apply的功能是彻底同样的,只是第二个参数不同;
call能够接收无限多个参数,apply只接收俩参数,而且第二个参数只能是argument。
“而它们一样的第一个参数,就是新的this指向!”
你先不用管引号里的话说明了什么,脑子里默记下这句话就行。
好了,如今,不要多想,往下看。app
我在实际应用中,最经常使用的就是用call、apply去“借”另外一个对象的方法来用,实际上是call、apply改变了this指向。复制代码
我写了个对象obj1,内部三个属性,两个数字numA、numB、还有个方法add,能够打印numA和numB之和:复制代码
var obj1 ={
numA:1,
numB:2,
add:function(){
console.log(this.numA + this.numB)
}
}
obj1.add(); //打印出obj1.numA和obj1.numB的和,即3复制代码
如今我写了个对象obj2,内部有只两个属性数字numA和数字numB,没有计算器,但也想求和,怎么办?
管obj1借啊!怎么借?call、apply啊!
上代码复制代码
var obj2 = {
numA:3,
numB:4
}
//用call借:
obj1.add.call(obj2); //打印出obj2.numA和obj2.numB的和,即7;
//用apply借:
obj1.add.apply(obj2); //打印出obj2.numA和obj2.numB的和,即7;复制代码
有意思吧?明明是obj1的add方法里出现了this,按照《理解JS中this指向的小技巧》中的思路,
找到的“.”左边是obj1,说明是obj1调用了add,add方法内部的this应该指向obj1啊!为啥算出来的结果都是obj2里的numA与numB之和呢?
由于用了call和apply啊!不是刚说完嘛,它们改变了this的指向啊,指向谁啊?第一个参数啊!第一个参数是谁啊?obj2啊!
因此你写obj1.add.call(obj2),add方法内部的this指向就变成了obj2,就打印出了obj2.numA和obj2.numB的和。
就起到了obj2向Obj1“借”了方法add的效果。复制代码
这个栗子是面向对象的栗子,对面向对象不够了解的同窗,请尽可能读懂不得不提的原型/原型链
函数
我写了个构造函数Obj1,内部三个属性,两个数字numA、numB、还有个方法add,能够打印numA和numB之和:复制代码
function Obj1(numA,numB){
this.numA = numA;
this.numB = numB;
}
Obj1.prototype.add = function(){
console.log(this.numA + this.numB)
}
var obj1 = new Obj1(1,2);
obj1.add(); //打印出obj1.numA和obj1.numB的和,即3复制代码
如今我写了个构造函数Obj2,内部有只两个属性数字numA和数字numB,没有计算器,但也想求和,怎么办?
管obj1借啊!怎么借?call、apply啊!
上代码复制代码
function Obj2(numA,numB){
this.numA = numA;
this.numB = numB;
}
var obj2 = new Obj2(3,4);
//用call向实例obj1借:
obj1.add.call(obj2,3,4); //打印出obj2.numA和obj2.numB的和,即7;
//用apply向实例obj1借:
obj1.add.apply(obj2,[3,4]); //打印出obj2.numA和obj2.numB的和,即7;
//用call向构造函数Obj1借:
Obj1.prototype.add.call(obj2, 3, 4); //打印出obj2.numA和obj2.numB的和,即7;
//用apply向构造函数Obj1借:
Obj1.prototype.add.apply(obj2, [3, 4]); //打印出obj2.numA和obj2.numB的和,即7;复制代码
这个栗子刚好说明了带参数的状况怎么“借”另外一个对象的方法,也把apply和call的不一样解释明白了,就是个传参不一样。
看这个 Obj1.prototype.add.call(obj2, 3, 4) ,眼熟吗?
像不像 Array.prototype.forEach.call(xxx) ?就是这么来的,xxx想借用Array.prototype的forEach方法完成遍历。复制代码
好比有个需求,须要作到每次调用先前别人写好的方法时,先在前面运行咱们添加的代码:
下面的代码不必定是最好的实现本需求的代码,但能够演示apply的应用。
生动的具体化一下:学习
function foo(){
console.log('我是陈海,我拍床戏去了');
}
foo();复制代码
如今侯亮平接手的反贪局接管了代码,
需求是,不改变陈海写的代码的状况下,在每次调用陈海写的代码时先打印一些话。复制代码
function beforeFoo(num){
console.log('侯亮平知道陈海有床戏,一共'+num+'场');
}
var fooOld = foo;
foo = function(num){
beforeFoo(num); //这里将会被陆亦可修改
fooOld();
}
foo(30); //运行一下看看效果复制代码
function beforeFoo(num,text){
console.log('侯亮平知道陈海有床戏,一共'+num+'场,',text);
}
var fooOld = foo;
foo = function(){
beforeFoo.apply(this,arguments); //陆亦可修改了这里
fooOld();
}
foo(30,'醒不过来'); //运行一下看看效果复制代码
刹车!陆亦可在她的代码里用到了apply!
咱们来分析一下她干了啥,完成了啥功能:
修改:把beforeFoo(num)改为beforeFoo.apply(this,arguments);
完成功能:beforeFoo能够任意修改参数个数,没必要再修改后续代码。复制代码
首先来看看beforeFoo.apply(this,arguments)中的this:
1、this出如今新foo的内部;
2、foo的调用语句是foo(30,'醒不过来'),是全局直接调用,找不到“.”;
根据我上一篇this教程,经过这两点,不难发现this指向window;
那么,根据本片文章前面提到过的,apply即“借”,beforeFoo.apply(this,arguments),
也就是this借用了beforeFoo方法,向谁借的?beforeFoo左边没有“.”,是全局调用,原来是向window借的!
而刚刚说过,此this指向window,这就好玩了:window向window借用了beforeFoo方法!
你说,那不就是beforeFoo直接调用吗,绕一圈干吗?别忘了还有arguments参数呢!
这么绕了一圈,在绕圈调用的过程当中,JS会解析arguments参数,自动用“,”帮你把参数分开传入beforeFoo方法,
之后不管你如何修改beforeFoo方法的参数个数,都不用再改剩余的代码了。
陆亦可利用这一点,巧妙的借助apply完成了代码的可用性提升。
PS:ES6新出的拓展符能够完成同样的效果:before.apply(this,arguments)能够写成before(...arguments);
请细细品味,发现道理都是想通的,有趣吧。
最后,侯亮平风骚的封装了代码,之后陈海不再怕不知道本身会演多少场床戏了。复制代码
PS:
欢迎转载,须要注明原址。
教程之间紧密联系,不懂的地方,请好好看下全系列教程目录,
有没有你不懂的那个关键字在里面。
若是帮到你,别忘了给我一颗Star~
ui