理解this、call仔夺、apply

this的指向

在js中琐脏,this總是回指向一個對象,需要注意的是,具體指向哪個對象實在運行時基于函數(shù)的執(zhí)行環(huán)境動態(tài)綁定的日裙,并非函數(shù)聲明時的環(huán)境吹艇。又或者說:this 永遠(yuǎn)指向最后調(diào)用它的那個對象

this的指向可以大致分為四種:

  • 作為對象的方法使用
  • 作為普通函數(shù)使用
  • 構(gòu)造函數(shù)調(diào)用
  • call阅签,apply方法調(diào)用

作為對象的方法調(diào)用

在 JavaScript 中, 函數(shù)就是對象掐暮。

當(dāng)函數(shù)作為對象的方法被調(diào)用時蝎抽,this指向該對象政钟。

var name = "globalName";
var obj = {
    name:  'objName',
    getName: function () {
        console.log(this.name);     
    }
}

obj.getName();   // objName

此時this指向最后調(diào)用他的對象,也就是obj樟结。

作為普通函數(shù)調(diào)用

當(dāng)函數(shù)不作為對象的屬性調(diào)用時养交,就是一個普通函數(shù),這樣的情況在 JavaScript 的在瀏覽器中的非嚴(yán)格模式默認(rèn)是屬于全局對象 window 的瓢宦,在嚴(yán)格模式碎连,就是 undefined。

JavaScript 嚴(yán)格模式(use strict)

var name = "globalName";
var obj = {
    name:  'objName',
    getName: function () {
        console.log(this.name);     
    }
}

var getName = obj.getName;
getName();    // globalName

此時雖然getName()是在對象obj中聲明的方法驮履,但是在實際執(zhí)行該方法的時候鱼辙,其調(diào)用對象已經(jīng)不是obj,而是全局對象 window玫镐,那么這時getName()就是作為一個普通函數(shù)調(diào)用倒戏。

構(gòu)造器調(diào)用

JavaScript中沒有類,但是可以通過構(gòu)造器創(chuàng)建對象恐似,如果函數(shù)調(diào)用前使用了new關(guān)鍵詞杜跷,則是調(diào)用了構(gòu)造函數(shù),返回的總是一個對象矫夷,這使得構(gòu)造器看起來像一個類葛闷。

// 構(gòu)造器
function constructor (name) {
    this.name = name;
}

// new object  
var obj = new constructor('objName');
obj.name;    // objName

但是使用構(gòu)造器創(chuàng)建對象時,需要注意一個問題双藕,如果構(gòu)造器顯示返回一個object類型的對象淑趾,那么此次new的結(jié)果會返回該對象。

// 構(gòu)造器
function constructor (name) {
    this.name = name;
    return {
        name: 'anotherName'
    }
}

// new object  
var obj = new constructor('objName');
obj.name;    // anotherName

call忧陪,apply方法調(diào)用

與前面的幾種方式相比治笨,使用call或者apply可以動態(tài)地修改傳入函數(shù)的this:

var obj1 = {
    name:  'obj1Name',
    getName: function () {
        console.log(this.name);     
    }
}

var obj2 = {
    name:  'obj2Name'
}

obj1.getName();    // obj1Name
obj1.getName.call(obj2);    // obj2Name
obj1.getName.apply(obj2);    // obj2Name

由于call或者apply方法改變了getName方法中this的指向,即將對象obj1的方法“借”給了對象obj2去執(zhí)行赤嚼。

call 和 apply

Function.prototype.call 和 Function.prototype.apply 在實際開發(fā)中旷赖,特別是在函數(shù)式風(fēng)格的代碼編寫中,非常有用更卒,去看大神的代碼等孵,也會經(jīng)常看到蹂空,應(yīng)用非常廣泛俯萌。

call 和 apply 的區(qū)別

Function.prototype.call 和 Function.prototype.apply 這兩個方法的作用一模一樣果录,區(qū)別在于傳參形式不同。

apply 一共只有兩個參數(shù):
第一個參數(shù) 是函數(shù)體中this指向的對象咐熙,
第二個參數(shù) 為一個帶下標(biāo)的集合弱恒,可以是數(shù)組或類數(shù)組,
apply 會把這個集合中的元素作為參數(shù)一一傳遞給被調(diào)用的函數(shù)棋恼。

var func = function (arg1, arg2) {
    console.log(arg1, arg2)
};
func.apply(obj, ['第一個參數(shù)', '第二個參數(shù)']); 

語法:func.apply(obj, [arg1, arg2, ...])

call 則參數(shù)數(shù)量不固定返弹,但是跟 apply 一樣
第一個參數(shù) 是函數(shù)體中this指向的對象
第二個參數(shù)開始,每個參數(shù)都會被一一傳遞給被調(diào)用的函數(shù)爪飘。

var func = function (arg1, arg2) {
    console.log(arg1, arg2)
};
func.call(obj, '第一個參數(shù)', '第二個參數(shù)'); 

語法:func.apply(obj, arg1, arg2, ...)

總結(jié)就是對參數(shù)是否用集合包裹起來的區(qū)別义起。但是apply的效率會比call高。

實際上上面的代碼塊執(zhí)行時是報錯的师崎,原因是obj并沒有定義默终,當(dāng)我們沒有目標(biāo)對象傳的時候,第一個參數(shù)可以傳null犁罩,函數(shù)體內(nèi)的this會指向默認(rèn)的宿主對象齐蔽,在瀏覽器中則為window

func.call(null, '第一個參數(shù)', '第二個參數(shù)');  // 等同于 window.func('第一個參數(shù)', '第二個參數(shù)')

call 和 apply的用途

改變this的指向

上面我們已經(jīng)說過call與apply可以用來改變函數(shù)體內(nèi)部this的指向?qū)ο螅窃趯嶋H開發(fā)中經(jīng)常會遇到this指向被不經(jīng)意改變的場景床估,那么這里再舉個?? 例子加深一下印象含滴。
比如有個div節(jié)點,節(jié)點的onclick事件中的this本來是指向這個div的:

<div id="id1">JS</div>
...
document.getElementById('id1').onclick = function() {
    console.log(this.id);  // id1
}

但如果變成這顷窒,在函數(shù)內(nèi)部增加一個函數(shù)func:

<div id="id1">JS</div>
...
document.getElementById('id1').onclick = function() {
    console.log(this.id);  // id1
    var func = function () {
        console.log(this.id); // undefined
    }
    func();
}

在onclick事件內(nèi)部調(diào)用func函數(shù)時蛙吏,func的this指向了window,而不是div鞋吉,這時候就可以用call鸦做,或者apply來修正this的指向。

<div id="id1">JS</div>
...
document.getElementById('id1').onclick = function() {
    console.log(this.id);  // id1
    var func = function () {
        console.log(this.id); // id1
    }
    func.apply(this);
}

另外谓着,大部分瀏覽器都實現(xiàn)了內(nèi)置的Function.prototype.bind泼诱,也是用來指定函數(shù)內(nèi)部的this指向,用法與call類似赊锚,只不過bind 是創(chuàng)建一個新的函數(shù)治筒,我們必須要手動去調(diào)用:

document.getElementById('id1').onclick = function() {
   console.log(this.id);  // id1
   var func = function () {
       console.log(this.id); // id1
   }
   func.bind(this)();
}

借用其他對象的方法

常見的有“借用構(gòu)造函數(shù)”,這樣可以實現(xiàn)一些類似繼承的效果:

var A = function (name) {
    this.name = name;
};
var B = function () {
    A.apply(this, arguments)
};
B.prototype.getName = function () {
 return this.name;
};
var b = new B('bClassName');
b.getName();  // bClassName

這里的B實際上就是借用了A構(gòu)造函數(shù)舷蒲,也就是構(gòu)造函數(shù)式繼承耸袜。

另外,下面這種運用場景會更密切一點牲平。

函數(shù)的參數(shù)arguments是一個類數(shù)組對象堤框,雖然有“下標(biāo)”,但不是真正的數(shù)組,自然不能使用obj.push(), obj.slice()等屬于Array的方法蜈抓。
那這種情況下启绰,我們可以使用Array.prototype對象上的方法來處理這樣的類數(shù)組對象。

例如:document.getElementsByClassName() 得到的就是一個HTMLCollection對象沟使,直接調(diào)用forEach的方式是不行的委可,這時候就可以找Array借一個forEach使用了。


image.png

再如

var a = {}
Array.prototype.push.call(a, 'first')

console.log(a.length); // 1
console.log(a[0]); // first
console.log(a); // {0: "first", length: 1}

這段代碼在絕大部分瀏覽器都能順利執(zhí)行腊嗡,在低版本的IE則需要顯示給a對象設(shè)置length屬性

類似上面的“借用”的方式還有很多着倾,這里就不贅述了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末叽唱,一起剝皮案震驚了整個濱河市屈呕,隨后出現(xiàn)的幾起案子微宝,更是在濱河造成了極大的恐慌棺亭,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蟋软,死亡現(xiàn)場離奇詭異镶摘,居然都是意外死亡,警方通過查閱死者的電腦和手機岳守,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門凄敢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人湿痢,你說我怎么就攤上這事涝缝。” “怎么了譬重?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵拒逮,是天一觀的道長。 經(jīng)常有香客問我臀规,道長滩援,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任塔嬉,我火速辦了婚禮玩徊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘谨究。我一直安慰自己恩袱,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布胶哲。 她就那樣靜靜地躺著畔塔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上俩檬,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天萎胰,我揣著相機與錄音,去河邊找鬼棚辽。 笑死技竟,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的屈藐。 我是一名探鬼主播榔组,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼联逻!你這毒婦竟也來了搓扯?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤包归,失蹤者是張志新(化名)和其女友劉穎锨推,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體公壤,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡换可,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了厦幅。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沾鳄。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖确憨,靈堂內(nèi)的尸體忽然破棺而出译荞,到底是詐尸還是另有隱情,我是刑警寧澤休弃,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布吞歼,位于F島的核電站,受9級特大地震影響玫芦,放射性物質(zhì)發(fā)生泄漏浆熔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一桥帆、第九天 我趴在偏房一處隱蔽的房頂上張望医增。 院中可真熱鬧,春花似錦老虫、人聲如沸叶骨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽忽刽。三九已至天揖,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間跪帝,已是汗流浹背今膊。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留伞剑,地道東北人斑唬。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像黎泣,于是被迫代替她去往敵國和親恕刘。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容