apply和call的用法

call 和 apply

EC3給Function的原型定義了兩個方法,它們是 Function.prototype.call 和 Function.prototype.apply涩馆。在實際的開發(fā)中允坚,特別是函數式編程風格的代碼中蛾号,call和apply尤為重要。能熟練的使用這兩個方法模式我們真正成為一名JavaScript程序員的重要一步展运。

call 和 apply 的區(qū)別

它們的作用其實是一模一樣的精刷,區(qū)別僅僅在于傳入的參數形式不同。

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

在實際開發(fā)中抵怎,經常會遇到this指向被不經意改變的場景,比如有一個div節(jié)點尝艘,div節(jié)點的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,更優(yōu)雅的做法可以這樣

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)

對于上面的代碼援雇,等式右邊的函數自執(zhí)行的結果為內部的匿名函數椎扬,但是執(zhí)行的時候相當于先把之前的 document.getElementById 保存到fn中了蚕涤,如下:

varfn=document.getElementById;

document.getElementById=function(){

returnfn.apply(document,arguments)//傳進來的實參在arguments中

}

然后當用變量再次存儲document.getElementById的時候這時候實際運行的是上面第二個等式后面的函數揖铜,然后返回的之前存儲的fn運行的結果,但是在函數執(zhí)行的時候贿肩,通過apply修正了this指向document。

2.Function.prototype.bind

大部分高級瀏覽器都實現(xiàn)了內置的Function.prototype.bind方法们何,用來指定內部的this指向控轿,它返回一個修改this之后的函數,但是并不會想apply和

call那樣直接執(zhí)行函數茬射,來看下面的代碼:

varobj={

fn(){

console.log(this);

}

}

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

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

那么咱們看看bind的實現(xiàn)原理是什么

Function.prototype.bind=function(context){

var_this=this;

returnfunction(){

return_this.apply(context,arguments);

}

}

也就是先把 之前的函數的引用保存起來在抛,然后返回一個新的函數,只不過這個函數在執(zhí)行的時候 返回的是保存的引用改變this之后的執(zhí)行結果肠阱。

3.借用其它對象的方法

我們都知道屹徘,杜鵑既不會筑巢衅金,也不會孵雛,而是把自己的蛋寄托給云雀等其他鳥類鉴吹,讓他們代為孵化和養(yǎng)育惩琉。同樣琳水,在JavaScript中也存在類似的借用現(xiàn)象。

借用方法的第一種場景是“借用構造函數”诚啃,通過這種技術始赎,可以實現(xiàn)一些類似繼承的效果:

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方法心软。這些借用其實很常見,沒什么好說的耳贬,那么他們內部實現(xiàn)的機制原理是什么呢咒劲? 不妨咱們翻開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身上存取其他數據挖帘,那么從下面的測試代碼可以發(fā)現(xiàn)完丽,一個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);

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市你稚,隨后出現(xiàn)的幾起案子瓷耙,更是在濱河造成了極大的恐慌朱躺,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件搁痛,死亡現(xiàn)場離奇詭異长搀,居然都是意外死亡,警方通過查閱死者的電腦和手機鸡典,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門源请,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人彻况,你說我怎么就攤上這事谁尸。” “怎么了纽甘?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵良蛮,是天一觀的道長。 經常有香客問我悍赢,道長决瞳,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任左权,我火速辦了婚禮皮胡,結果婚禮上,老公的妹妹穿的比我還像新娘赏迟。我一直安慰自己屡贺,他們只是感情好,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布锌杀。 她就那樣靜靜地躺著甩栈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪抛丽。 梳的紋絲不亂的頭發(fā)上谤职,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機與錄音亿鲜,去河邊找鬼。 笑死冤吨,一個胖子當著我的面吹牛蒿柳,可吹牛的內容都是我干的。 我是一名探鬼主播漩蟆,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼垒探,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了怠李?” 一聲冷哼從身側響起圾叼,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤蛤克,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后夷蚊,有當地人在樹林里發(fā)現(xiàn)了一具尸體构挤,經...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年惕鼓,在試婚紗的時候發(fā)現(xiàn)自己被綠了筋现。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡箱歧,死狀恐怖矾飞,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情呀邢,我是刑警寧澤洒沦,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站价淌,受9級特大地震影響微谓,放射性物質發(fā)生泄漏。R本人自食惡果不足惜输钩,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一豺型、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧买乃,春花似錦姻氨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至功戚,卻和暖如春娶眷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背啸臀。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工届宠, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人乘粒。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓豌注,卻偏偏與公主長得像,于是被迫代替她去往敵國和親灯萍。 傳聞我的和親對象是個殘疾皇子轧铁,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內容

  • 單例模式 適用場景:可能會在場景中使用到對象,但只有一個實例旦棉,加載時并不主動創(chuàng)建齿风,需要時才創(chuàng)建 最常見的單例模式药薯,...
    Obeing閱讀 2,056評論 1 10
  • 工廠模式類似于現(xiàn)實生活中的工廠可以產生大量相似的商品,去做同樣的事情救斑,實現(xiàn)同樣的效果;這時候需要使用工廠模式童本。簡單...
    舟漁行舟閱讀 7,718評論 2 17
  • 我在一開始看到javascript的函數apply和call時,非常的模糊,看也看不懂,最近在網上看到一些文章對a...
    Joe_Somebody閱讀 402評論 0 0
  • 第一部分 準入訓練 第1章 進入忍者世界 js開發(fā)人員通常使用js庫來實現(xiàn)通用和可重用的功能。這些庫需要簡單易用系谐,...
    如201608閱讀 1,334評論 1 2
  • 三巾陕、閉包和高階函數 3.1 閉包 3.1.1 變量的作用域 所謂變量的作用域,就是變量的有效范圍纪他。通過作用域的劃分...
    梁同學de自言自語閱讀 1,445評論 0 6