2. this嫡意、call和apply

本文源于本人關(guān)于《JavaScript設(shè)計(jì)模式與開(kāi)發(fā)實(shí)踐》(曾探著)的閱讀總結(jié)。想詳細(xì)了解具體內(nèi)容建議閱讀該書捣辆。

1. this

this總是指向一個(gè)對(duì)象蔬螟,而具體指向哪個(gè)對(duì)象是在運(yùn)行時(shí)基于函數(shù)的執(zhí)行環(huán)境動(dòng)態(tài)綁定的。

this的指向

除去with和eval汽畴,實(shí)際應(yīng)用中旧巾,this指向分為以下四類:

  • 作為對(duì)象的方法調(diào)用
  • 作為普通函數(shù)調(diào)用
  • 構(gòu)造器調(diào)用
  • Function.prototype.call & Function.prototype.apply
作為對(duì)象的方法調(diào)用
var obj = {
    a: 1,
    getA: function(){
        console.log(this === obj); // true
        console.log(this.a); // 1
    }
}

obj.getA();

這個(gè)時(shí)候,由于是obj調(diào)用的getA()函數(shù)忍些,故this指向obj鲁猩。

作為普通函數(shù)調(diào)用
var obj = {
    name: aaaa,
    getName: function(){
        console.log(this === obj); // false
        console.log(this.name); // yozo
    }
}

window.name = yozo;
var getGlobalName = function(){
    console.log(this.name);
}

var getName = obj.getName;

getGlobalName(); // yozo;
getName();

只要函數(shù)沒(méi)有調(diào)用者,則this就指向全局對(duì)象罢坝,在瀏覽器中的Js里廓握,全局對(duì)象是window。嚴(yán)格模式中嘁酿,this不指向全局對(duì)象疾棵。
這里主要看getName,它本身只是一個(gè)函數(shù)痹仙,由于被抽離出來(lái)了,故執(zhí)行getName()時(shí)殉了,它是沒(méi)有調(diào)用者的开仰,和getGlobalName一樣,作為普通函數(shù)調(diào)用。

構(gòu)造器調(diào)用
var Myclass = function(){
    this.name = 'yozo';
};

var obj = new Myclass();
console.log(obj.name); // yozo

這個(gè)時(shí)候的this代表被新創(chuàng)建出來(lái)的那個(gè)對(duì)象众弓,這里指obj恩溅。

但是:

var Myclass = function(){
    this.name = 'yozo';
    return {
        name: 'ann'
    }
};

var obj = new Myclass();
console.log(obj.name); // ann

當(dāng)顯示返回一個(gè)對(duì)象時(shí),那么這個(gè)時(shí)候顯示返回的這個(gè)對(duì)象會(huì)被obj引用谓娃,故這個(gè)時(shí)候 obj.name就是ann而不是yozo脚乡。

Function.prototype.call & Function.prototype.apply
var obj1 = {
    name: 'yozo',
    getName: function(){
        console.log(this.name);
    }
}

var obj2 = {
    name: 'ann'
}

console.log(obj1.getName()); // yozo
console.log(obj1.getName.call(obj2)); // ann
  • 第一個(gè)console中,getName的調(diào)用者是obj1滨达,則this指向obj1奶稠;
  • 第二個(gè)console中,getName的調(diào)用者本是obj1捡遍,但是這個(gè)時(shí)候出現(xiàn)了.call(obj2)锌订。就好像是打電話告訴obj2:“喂,obj2画株,你去執(zhí)行obj1的getName方法吧”辆飘,故這個(gè)時(shí)候調(diào)用者就變成了obj2,我們知道谓传,this總是指向調(diào)用者蜈项,故這個(gè)時(shí)候this就指代obj2。

2. call和apply

call和apply的區(qū)別

剛剛我們已經(jīng)說(shuō)了call的作用了续挟,就是改變函數(shù)的調(diào)用者紧卒。那么apply的作用呢?也是改變函數(shù)的調(diào)用者庸推。區(qū)別在于常侦,call和apply都是Function.prototype上的方法,故只有函數(shù)才能調(diào)用這兩個(gè)函數(shù)贬媒, 既然是函數(shù)的call和apply聋亡,那么函數(shù)在執(zhí)行的時(shí)候總會(huì)有參數(shù)吧,那么改變了調(diào)用者之后际乘,函數(shù)的參數(shù)怎么處理坡倔?

  • call: 把參數(shù)一個(gè)一個(gè)列舉出來(lái)(不固定參數(shù),第一個(gè)為新的調(diào)用者脖含,其他的為傳入該函數(shù)的其他參數(shù))
  • apply: 把參數(shù)一鍋傳(只有兩個(gè)參數(shù)罪塔,第一個(gè)為新的調(diào)用者,另一個(gè)參數(shù)為參數(shù)數(shù)組)
var func = function(a, b, c){
    console.log([a, b, c]);
}

func.apply(null,[1, 2, 3]);
func.call(null, 1, 2, 3);

這個(gè)函數(shù)沒(méi)this养葵,所以不需要調(diào)用者征堪,我們只用來(lái)區(qū)別call和apply的用法:

  • call:從第二個(gè)參數(shù)開(kāi)始,一一對(duì)應(yīng)func的各個(gè)參數(shù)
  • apply:參數(shù)數(shù)組與func的各個(gè)參數(shù)一一對(duì)應(yīng)关拒。
call和apply用途
  • 改變this指向(改變調(diào)用者)
document.getElementById('div1').onclick = function(){
    console.log(this.id); // div1
    var func = function(){
        console.log(this.id);
    }
    func(); // undefined (無(wú)調(diào)用者)
    func.call(this); // div1 
}

onclick回調(diào)函數(shù)中this表示document.getElementById('div1')佃蚜, 但是執(zhí)行func()的時(shí)候沒(méi)有調(diào)用者庸娱,默認(rèn)調(diào)用者為window,但是我們希望它仍能保持指向document.getElementById('div1')谐算,則把this傳給了func熟尉,讓他調(diào)用該函數(shù)。

  • Function.prototype.bind:將一個(gè)函數(shù)的this固定為某個(gè)對(duì)象洲脂,之后使用時(shí)就不用再使用call和apply了斤儿。
    簡(jiǎn)易版:
Function.prototype.bind = function (context) {
  var self = this;
  return function () {
    self.apply(context, arguments);
  }
}

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

var obj2 = {
  name: 'ann'
}

var obj2getName = obj1.getName.bind(obj2);
obj2getName(); // ann

這時(shí)的this就已經(jīng)被固定為obj2了。

這個(gè)版本在固定對(duì)象時(shí)不能保存參數(shù)恐锦,故升級(jí)版:

Function.prototype.bind = function () {
  var self = this; // 保留該函數(shù)的調(diào)用者
  var context = [].shift.call(arguments); // 獲取參數(shù)數(shù)組中第一個(gè)參數(shù)往果,即需要被固定的那個(gè)對(duì)象
  var args = [].slice.call(arguments); // 保存剩下的參數(shù)
  
  // 返回一個(gè)新的函數(shù)
  return function () {
    // 改變調(diào)用者為需要被固定的那個(gè)對(duì)象,將原本剩下的參數(shù)踩蔚,和新傳入的參數(shù)組成一個(gè)參數(shù)數(shù)組并執(zhí)行
    self.apply(context, [].concat.call(args, [].slice.call(arguments)));
  }
}

var obj = {
  name: 'yozo'
}

var func = function (a, b, c, d) {
  console.log(this.name);
  console.log([a, b, c, d]);
}.bind(obj, 1, 2);

func(3, 4); // 1, 2, 3, 4
  • 借用其他對(duì)象的方法:之前的obj1.getName.call(obj2)就是obj2借用obj1的方法棚放。我們常用的是借用Object或者Array的prototype的方法:
(function(){
    Array.prototype.push.call(arguments, 3, 4);
    console.log(arguments);
})(1, 2)

// 1, 2, 3, 4

這其實(shí)是arguments參數(shù)數(shù)組對(duì)象利用了真數(shù)組對(duì)象,參數(shù)數(shù)組為偽數(shù)組(DOM節(jié)點(diǎn)數(shù)組也是)馅闽,但是偽數(shù)組可以借用真數(shù)組的方法飘蚯。

偽數(shù)組需要滿足兩個(gè)條件:

  1. 對(duì)象本身要可以存取屬性
  2. 對(duì)象length屬性可以讀寫
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市福也,隨后出現(xiàn)的幾起案子局骤,更是在濱河造成了極大的恐慌,老刑警劉巖暴凑,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件峦甩,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡现喳,警方通過(guò)查閱死者的電腦和手機(jī)凯傲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)嗦篱,“玉大人冰单,你說(shuō)我怎么就攤上這事【拇伲” “怎么了诫欠?”我有些...
    開(kāi)封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)浴栽。 經(jīng)常有香客問(wèn)我荒叼,道長(zhǎng),這世上最難降的妖魔是什么典鸡? 我笑而不...
    開(kāi)封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任被廓,我火速辦了婚禮,結(jié)果婚禮上萝玷,老公的妹妹穿的比我還像新娘伊者。我一直安慰自己英遭,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布亦渗。 她就那樣靜靜地躺著,像睡著了一般汁尺。 火紅的嫁衣襯著肌膚如雪法精。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天痴突,我揣著相機(jī)與錄音搂蜓,去河邊找鬼。 笑死辽装,一個(gè)胖子當(dāng)著我的面吹牛帮碰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播拾积,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼殉挽,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了拓巧?” 一聲冷哼從身側(cè)響起斯碌,我...
    開(kāi)封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎肛度,沒(méi)想到半個(gè)月后傻唾,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡承耿,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年冠骄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片加袋。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡凛辣,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出锁荔,到底是詐尸還是另有隱情蟀给,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布阳堕,位于F島的核電站跋理,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏恬总。R本人自食惡果不足惜前普,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望壹堰。 院中可真熱鬧拭卿,春花似錦骡湖、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至惠桃,卻和暖如春浦夷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背辜王。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工劈狐, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人呐馆。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓肥缔,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親汹来。 傳聞我的和親對(duì)象是個(gè)殘疾皇子续膳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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