apply和call的用法

call 和 apply

EC3给Function的原型定义了两个方法,它们是 Function.prototype.call 和 Function.prototype.apply。在实际的开发中,特别是函数式编程风格的代码中,call和apply尤为重要。能熟练的使用这两个方法模式我们真正成为一名JavaScript程序员的重要一步。

call 和 apply 的区别

它们的作用其实是一模一样的,区别仅仅在于传入的参数形式不同。

apply 接受两个参数,第一个参数用来制定函数体内this的指向,第二个参数为一个带下标的集合,这个集合可以为数组,也可以为类数组,apply方法把这个集合中的元素作为参数传递给被调用的函数。

varfn=function(a,b,c){

alert([a,b,c,]);// [1,2,3]

};

fn.apply(null,[1,2,3])

call 传入的参数数量不固定,第一个参用来制定函数体内的this指向,从第二个参数开始,每个参数被依次传入函数体内。

varfn=function(a,b,c){

alert([1,2,3])

}

当使用 call 或者 apply 时,如果我们传入的第一个参数为null,函数体内的this会默认指向宿主对象,在浏览器中,如果使用严格模式,则还为null。

varfn=function(){

alert(this===window)//true

}

fn.call(null)

varfn2=function(){

"use strict"

alert(this===null)//true

}

fn2.call(null)

call 和 apply 的用途

1.改变this指向,直接看代码

varobj1={

name:"fq"

};

varobj2={

name:"mm"

}

window.name='window';

vargetName=function(){

alert(this.name)

}

getName()// window

getName.call(obj1)//fq

getName.call(obj2)//mm

在实际开发中,经常会遇到this指向被不经意改变的场景,比如有一个div节点,div节点的onclick事件中的this本来是指向这个div的。

document.getElementById('div').onclick=function(){

alert(this.id)//div

}

假设该事件函数中有一个内部的函数fn,在事件内部调用fn函数时,fn函数体内的this就指向了window,而不是我们预期的div,这个时候我们就可以用call 和 apply去改变this指向了。

document.getElementById('div').onclick=function(){

alert(this.id)//div

varfn=function(){

alert(this.id)//undefined

};

fn();

};

//之前都是保存一下this,更优雅的做法可以这样

document.getElementById('div').onclick=function(){

alert(this.id)//div

varfn=function(){

alert(this.id)//undefined

};

fn.call(this);

};

案例:内部丢失的this

或许你某天会觉得 document.getElementById函数有点太长了,也去你会这么做:

vargetId=document.getElementById;

getId('div');//但是会报错...

这是因为document.getElementById内部的this实际上在调用的时候 是需要指向document的,所以我们需要手动修正this

document.getElementById=(function(fn){

returnfunction(){

returnfn.apply(document,arguments);

}

})(document.getElementById)

对于上面的代码,等式右边的函数自执行的结果为内部的匿名函数,但是执行的时候相当于先把之前的 document.getElementById 保存到fn中了,如下:

varfn=document.getElementById;

document.getElementById=function(){

returnfn.apply(document,arguments)//传进来的实参在arguments中

}

然后当用变量再次存储document.getElementById的时候这时候实际运行的是上面第二个等式后面的函数,然后返回的之前存储的fn运行的结果,但是在函数执行的时候,通过apply修正了this指向document。

2.Function.prototype.bind

大部分高级浏览器都实现了内置的Function.prototype.bind方法,用来指定内部的this指向,它返回一个修改this之后的函数,但是并不会想apply和

call那样直接执行函数,来看下面的代码:

varobj={

fn(){

console.log(this);

}

}

setTimeout(obj.fn,1000);//window

setTimeout(obj.fn.bind(obj),1000);//obj

那么咱们看看bind的实现原理是什么

Function.prototype.bind=function(context){

var_this=this;

returnfunction(){

return_this.apply(context,arguments);

}

}

也就是先把 之前的函数的引用保存起来,然后返回一个新的函数,只不过这个函数在执行的时候 返回的是保存的引用改变this之后的执行结果。

3.借用其它对象的方法

我们都知道,杜鹃既不会筑巢,也不会孵雏,而是把自己的蛋寄托给云雀等其他鸟类,让他们代为孵化和养育。同样,在JavaScript中也存在类似的借用现象。

借用方法的第一种场景是“借用构造函数”,通过这种技术,可以实现一些类似继承的效果:

varA=function(name){

console.log(name)

};

varB=function(){

A.apply(this,agruments);

};

B.prototype.getName=function(){

console.log(this.name)

}

varb=newB('momo');

b.getName();// momo

借用方法的第二种场景跟我们更加密切。

函数的参数列表arguments是一个类数组的对象,虽然它也有“小标”,但它并非正在的数组,所以不能像数组一样进行排序操作或者往集合里面添加一个新元素。这种情况下,我们常常会借用Array.prototype对象上的方法。比如想往arguments中添加一个新元素,通常会借用Array.prototype.push;

(function(){

Array.prototype.push.call(arguments,3);

console.log(arguments);// [1, 2, 3]

})(1,2)

在操作arguments的时候我们经常频繁的去找Array.prototype对象借用方法。

想把arguments转换成真正的数组的时候,可以借用Array.prototype.slice方法,想截取arguments列表中第一个元素的时候,由可以借用Array.prototype.shift方法。这些借用其实很常见,没什么好说的,那么他们内部实现的机制原理是什么呢? 不妨咱们翻开v8引擎的源码来看看吧!

functionArrayPush(){

varn=TO_UINT32(this.length);//被push对象的length

varm=%_ArgumentsLength();//push的参数个数

for(vari=0;i

this[i+n]=%_Arguments[i];//赋值元素

}

this.length=m+n;

returnthis.length;

}

通过上面这段代码可以看到,Array.prototype.push实际上是一个属性赋值的过过程,把参数按照下标依次添加到被push的对象上面,顺便修改了这个对象的length属性。至于被修改的对象是谁,到底是个数组还是个对象,这个并不重要。

那么改写成 JavaScript 的代码 push 应该是这样的

varUtils={

push(){

varn=arguments[0].length||0,

m=arguments.length-1;

for(vari=0;i

arguments[0][i+n]=arguments[i+1]

}

arguments[0].length=m+n;

returnarguments[0].length;

}

}

varo={};

Utils.push(o,1,2,3);// 3

console.log(o);//Object {0: 1, 1: 2, 2: 3, length: 3}

由此可以推断我们可以把“任意”的对象传入Array.prototype.push。为什么要把“任意”这两个字加引号呢? 因为这个对象其实还要满足2各条件:

对象本身可以存储属性

对象的length属性可读可写

对于第一个条件,对象本身存取属性并没有问题,但是如果借用Array.prototype.push方法的不是一个Object类型数据,而是一个number类型的数据呢?我们无法在number身上存取其他数据,那么从下面的测试代码可以发现,一个number类型的数据不可能借用到这个方法:

vara=1;

Array.prototype.push.call(a,'first');

alert(a.length)// undefined

alert(a[0])//undefined

对于第二个条件,函数的length属性就是只读的,表示形参的个数,我们尝试把一个函数当做this传入Array.prototype.push:

varfn=function(){};

Array.prototype.push.call(fn,'first');//报错

alert(fn.length);

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容