this 指向詳細(xì)解析(箭頭函數(shù))

本文地址:http://www.reibang.com/p/16dd8acb0b13

前言

this 指向問題是入坑前端必須了解知識點(diǎn),現(xiàn)在迎來了ES6時(shí)代,因?yàn)榧^函數(shù)的出現(xiàn),所以感覺有必要對 this 問題梳理一下氨淌,遂有此文

在非箭頭函數(shù)下召边, this 指向調(diào)用其所在函數(shù)的對象稳吮,而且是離誰近就是指向誰(此對于常規(guī)對象娱俺,原型鏈, getter & setter等都適用)废麻;構(gòu)造函數(shù)下荠卷,this與被創(chuàng)建的新對象綁定;DOM事件烛愧,this指向觸發(fā)事件的元素油宜;內(nèi)聯(lián)事件分兩種情況,bind綁定怜姿, call & apply 方法等慎冤, 容以下一步一步討論。箭頭函數(shù)也會穿插其中進(jìn)行討論沧卢。

全局環(huán)境下

在全局環(huán)境下蚁堤,this 始終指向全局對象(window), 無論是否嚴(yán)格模式;

console.log(this.document === document); // true

// 在瀏覽器中但狭,全局對象為 window 對象:
console.log(this === window); // true

this.a = 37;
console.log(window.a); // 37

函數(shù)上下文調(diào)用

函數(shù)直接調(diào)用

普通函數(shù)內(nèi)部的this分兩種情況披诗,嚴(yán)格模式和非嚴(yán)格模式。

嚴(yán)格模式下立磁,this 的值默認(rèn)為全局對象window

function f1(){
  return this;
}

f1() === window; // true

非嚴(yán)格模式呈队, this的值為undefined

function f2(){
  "use strict"; // 這里是嚴(yán)格模式
  return this;
}

f2() === undefined; // true

對象中的this

對象內(nèi)部方法的this指向調(diào)用這些方法的對象,

  1. 函數(shù)的定義位置不影響其this指向唱歧,this指向只和調(diào)用函數(shù)的對象有關(guān)宪摧。
  2. 多層嵌套的對象粒竖,內(nèi)部方法的this指向離被調(diào)用函數(shù)最近的對象(window也是對象,其內(nèi)部對象調(diào)用方法的this指向內(nèi)部對象几于, 而非window)蕊苗。
//1
var o = {
  prop: 37,
  f: function() {
    return this.prop;
  }
};
console.log(o.f());  //37
var a = o.f;
console.log(a()):  //undefined

var o = {prop: 37};

function independent() {
  return this.prop;
}

o.f = independent;

console.log(o.f()); // logs 37

//2
o.b = {
  g: independent,
  prop: 42
};
console.log(o.b.g()); // logs 42

原型鏈中this

原型鏈中的方法的this仍然指向調(diào)用它的對象,與以上討論一致孩革;看個(gè)例子岁歉,

var o = {
  f : function(){ 
    return this.a + this.b; 
  }
};
var p = Object.create(o);
p.a = 1;
p.b = 4;

console.log(p.f()); // 5

可以看出, 在p中沒有屬性f膝蜈,當(dāng)執(zhí)行p.f()時(shí)锅移,會查找p的原型鏈,找到 f 函數(shù)并執(zhí)行饱搏,但這與函數(shù)內(nèi)部this指向?qū)ο?p 沒有任何關(guān)系非剃,只需記住誰調(diào)用指向誰。

以上對于函數(shù)作為getter & setter 調(diào)用時(shí)同樣適用推沸。

構(gòu)造函數(shù)中this

構(gòu)造函數(shù)中的this與被創(chuàng)建的新對象綁定备绽。

注意:當(dāng)構(gòu)造器返回的默認(rèn)值是一個(gè)this引用的對象時(shí),可以手動(dòng)設(shè)置返回其他的對象鬓催,如果返回值不是一個(gè)對象肺素,返回this。

function C(){
  this.a = 37;
}

var o = new C();
console.log(o.a); // logs 37


function C2(){
  this.a = 37;
  return {a:38};
}

var b = new C2();
console.log(b.a); // logs 38

以上兩個(gè)例子內(nèi)部的this都指向?qū)ο髈, 看到這里的你不妨在控制臺執(zhí)行下以上代碼宇驾,看看對象 o 和 b 倍靡,這些是屬于構(gòu)造函數(shù)的內(nèi)容了,此處不多做介紹课舍。(C2函數(shù)中的this.a = 37 對整個(gè)過程完全沒有影響的塌西, 可以被忽略的)。

call & apply

當(dāng)函數(shù)通過Function對象的原型中繼承的方法 call() 和 apply() 方法調(diào)用時(shí)筝尾, 其函數(shù)內(nèi)部的this值可綁定到 call() & apply() 方法指定的第一個(gè)對象上捡需, 如果第一個(gè)參數(shù)不是對象,JavaScript內(nèi)部會嘗試將其轉(zhuǎn)換成對象然后指向它筹淫。

例子:

function add(c, d){
  return this.a + this.b + c + d;
}

var o = {a:1, b:3};

add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16

add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34

function tt() {
  console.log(this);
}
// 返回對象見下圖(圖1)
tt.call(5);  // Number {[[PrimitiveValue]]: 5} 
tt.call('asd'); // String {0: "a", 1: "s", 2: "d", length: 3, [[PrimitiveValue]]: "asd"}

bind 方法

bind方法在ES5引入站辉, 在Function的原型鏈上, Function.prototype.bind损姜。通過bind方法綁定后庵寞, 函數(shù)將被永遠(yuǎn)綁定在其第一個(gè)參數(shù)對象上, 而無論其在什么情況下被調(diào)用薛匪。

function f(){
  return this.a;
}

var g = f.bind({a:"azerty"});
console.log(g()); // azerty

var o = {a:37, f:f, g:g};
console.log(o.f(), o.g()); // 37, azerty

DOM 事件處理函數(shù)中的 this & 內(nèi)聯(lián)事件中的 this

DOM事件處理函數(shù)

  1. 當(dāng)函數(shù)被當(dāng)做監(jiān)聽事件處理函數(shù)時(shí)捐川, 其 this 指向觸發(fā)該事件的元素 (針對于addEventListener事件)
  // 被調(diào)用時(shí),將關(guān)聯(lián)的元素變成藍(lán)色
    function bluify(e){
      //在控制臺打印出所點(diǎn)擊元素
      console.log(this);
      //阻止時(shí)間冒泡
      e.stopPropagation();
      //阻止元素的默認(rèn)事件
      e.preventDefault();      
      this.style.backgroundColor = '#A5D9F3';
    }

    // 獲取文檔中的所有元素的列表
    var elements = document.getElementsByTagName('*');

    // 將bluify作為元素的點(diǎn)擊監(jiān)聽函數(shù)逸尖,當(dāng)元素被點(diǎn)擊時(shí)古沥,就會變成藍(lán)色
    for(var i=0 ; i<elements.length ; i++){
      elements[i].addEventListener('click', bluify, false);
    }

以上代碼建議在網(wǎng)頁中執(zhí)行以下瘸右,看下效果。

內(nèi)聯(lián)事件
內(nèi)聯(lián)事件中的this指向分兩種情況:

  1. 當(dāng)代碼被內(nèi)聯(lián)處理函數(shù)調(diào)用時(shí)岩齿,它的this指向監(jiān)聽器所在的DOM元素
  2. 當(dāng)代碼被包括在函數(shù)內(nèi)部執(zhí)行時(shí)太颤,其this指向等同于 ****函數(shù)直接調(diào)用****的情況,即在非嚴(yán)格模式指向全局對象window盹沈, 在嚴(yán)格模式指向undefined

頁面的代碼塊

在瀏覽器內(nèi)顯示三個(gè)按鈕

依次點(diǎn)擊上邊的三個(gè)按鈕后在控制臺的輸出結(jié)果龄章,

建議自己操作一遍以便于更好的理解。

setTimeout & setInterval

對于延時(shí)函數(shù)內(nèi)部的回調(diào)函數(shù)的this指向全局對象window(當(dāng)然我們可以通過bind方法改變其內(nèi)部函數(shù)的this指向)
看下邊代碼及截圖

//默認(rèn)情況下代碼
function Person() {  
    this.age = 0;  
    setTimeout(function() {
        console.log(this);
    }, 3000);
}

var p = new Person();//3秒后返回 window 對象
==============================================
//通過bind綁定
function Person() {  
    this.age = 0;  
    setTimeout((function() {
        console.log(this);
    }).bind(this), 3000);
}

var p = new Person();//3秒后返回構(gòu)造函數(shù)新生成的對象 Person{...}

箭頭函數(shù)中的 this

由于箭頭函數(shù)不綁定this乞封, 它會捕獲其所在(即定義的位置)上下文的this值做裙, 作為自己的this值,

  1. 所以 call() / apply() / bind() 方法對于箭頭函數(shù)來說只是傳入?yún)?shù)肃晚,對它的 this 毫無影響锚贱。
  2. 考慮到 this 是詞法層面上的,嚴(yán)格模式中與 this 相關(guān)的規(guī)則都將被忽略关串。(可以忽略是否在嚴(yán)格模式下的影響)

因?yàn)榧^函數(shù)可以捕獲其所在上下文的this值 所以

function Person() {  
    this.age = 0;  
    setInterval(() => {
        // 回調(diào)里面的 `this` 變量就指向了期望的那個(gè)對象了
        this.age++;
    }, 3000);
}

var p = new Person();

以上代碼可以得到我們所以希望的值拧廊,下圖可以看到,在setTimeout中的this指向了構(gòu)造函數(shù)新生成的對象晋修,而普通函數(shù)指向了全局window對象

var adder = {
  base : 1,
    
  add : function(a) {
    var f = v => v + this.base;
    return f(a);
  },

  addThruCall: function inFun(a) {
    var f = v => v + this.base;
    var b = {
      base : 2
    };
            
    return f.call(b, a);
  }
};

console.log(adder.add(1));         // 輸出 2
console.log(adder.addThruCall(1)); // 仍然輸出 2(而不是3吧碾,其內(nèi)部的this并沒有因?yàn)閏all() 而改變,其this值仍然為函數(shù)inFun的this值墓卦,指向?qū)ο骯dder

bind() & apply() 讀者可以自行測試

對于是否嚴(yán)格模式示例代碼(可以復(fù)制進(jìn)控制臺進(jìn)行驗(yàn)證)

var f = () => {'use strict'; return this};
var p = () => { return this};
console.log(1,f() === window);
console.log(2,f() === p());
//1 true
//2 true

以上的箭頭函數(shù)都是在方法內(nèi)部倦春,總之都是以非方法的方式使用,如果將箭頭函數(shù)當(dāng)做一個(gè)方法使用會怎樣呢趴拧?
上例子

var obj = {
  i: 10,
  b: () => console.log(this.i, this),
  c: function() {
    console.log( this.i, this)
  }
}
obj.b();  // undefined window{...}
obj.c();  // 10 Object {...}

可以看到,作為方法的箭頭函數(shù)this指向全局window對象山叮,而普通函數(shù)則指向調(diào)用它的對象

以上為個(gè)人學(xué)習(xí)整理內(nèi)容著榴, 文中例子參考MDN, 歡迎交流學(xué)習(xí)

參考文獻(xiàn)

this - JavaScript | MDN
箭頭函數(shù) - JavaScript | MDN

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末屁倔,一起剝皮案震驚了整個(gè)濱河市脑又,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌锐借,老刑警劉巖问麸,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異钞翔,居然都是意外死亡严卖,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進(jìn)店門布轿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哮笆,“玉大人来颤,你說我怎么就攤上這事〕碇猓” “怎么了福铅?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長项阴。 經(jīng)常有香客問我滑黔,道長,這世上最難降的妖魔是什么环揽? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任略荡,我火速辦了婚禮,結(jié)果婚禮上薯演,老公的妹妹穿的比我還像新娘撞芍。我一直安慰自己,他們只是感情好跨扮,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布序无。 她就那樣靜靜地躺著,像睡著了一般衡创。 火紅的嫁衣襯著肌膚如雪帝嗡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天璃氢,我揣著相機(jī)與錄音哟玷,去河邊找鬼。 笑死一也,一個(gè)胖子當(dāng)著我的面吹牛巢寡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播椰苟,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼抑月,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了舆蝴?” 一聲冷哼從身側(cè)響起谦絮,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎洁仗,沒想到半個(gè)月后层皱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赠潦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年叫胖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片她奥。...
    茶點(diǎn)故事閱讀 40,102評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡臭家,死狀恐怖疲陕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情钉赁,我是刑警寧澤蹄殃,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站你踩,受9級特大地震影響诅岩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜带膜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一吩谦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧膝藕,春花似錦式廷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至袜爪,卻和暖如春蠕趁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背辛馆。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工俺陋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人昙篙。 一個(gè)月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓腊状,卻偏偏與公主長得像,于是被迫代替她去往敵國和親苔可。 傳聞我的和親對象是個(gè)殘疾皇子缴挖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評論 2 355

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