this指向入門完整版 - 王云飛

this指向

與其他語言相比,函數(shù)的this關(guān)鍵字在 JavaScript 中的表現(xiàn)略有不同,此外,在嚴(yán)格模式和非嚴(yán)格模式之間也會有一些差別。

在絕大多數(shù)情況下,函數(shù)的調(diào)用方式?jīng)Q定了this的值。this是在運(yùn)行時(shí)基于函數(shù)的執(zhí)行環(huán)境綁定的:在全局函數(shù)中this等于window,而當(dāng)函數(shù)被作為某個(gè)對象方法調(diào)用時(shí),this等于那個(gè)對象。ES5引入了bind方法來設(shè)置函數(shù)的this值,而不用考慮函數(shù)如何被調(diào)用的。

全局環(huán)境

無論是否在嚴(yán)格模式下,在全局執(zhí)行環(huán)境中(在任何函數(shù)體外部)this 都指向全局對象。

// 在瀏覽器中, window 對象同時(shí)也是全局對象:
console.log(this === window); // true

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

this.b = "MDN";
console.log(window.b)  // "MDN"
console.log(b)         // "MDN"

函數(shù)(運(yùn)行內(nèi))環(huán)境

在函數(shù)內(nèi)部,this的值取決于函數(shù)被調(diào)用的方式。
普通函數(shù)內(nèi)部的this分兩種情況,嚴(yán)格模式和非嚴(yán)格模式。

  • 非嚴(yán)格模式下,this 默認(rèn)指向全局對象window
function f1(){
  return this;
}
//在瀏覽器中:
f1() === window;   //在瀏覽器中,全局對象是window

//在Node中:
f1() === global;

然而,在嚴(yán)格模式下,this將會默認(rèn)為undefined。

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

f2() === undefined; // true

作為對象的方法

當(dāng)函數(shù)作為對象里的方法被調(diào)用時(shí),它們的this是調(diào)用該函數(shù)的對象。

  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)。
    下面的例子中,當(dāng)o.f()被調(diào)用時(shí),函數(shù)內(nèi)的this將綁定到o對象。
var o = {
  prop: 37,
  f: function() {
    return this.prop;
  }
};

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

請注意,這樣的行為,根本不受函數(shù)定義方式或位置的影響。在前面的例子中,我們在定義對象o的同時(shí),將函數(shù)內(nèi)聯(lián)定義為成員 f 。但是,我們也可以先定義函數(shù),然后再將其附屬到o.f。這樣做會導(dǎo)致相同的行為:

var o = {prop: 37};

function independent() {
  return this.prop;
}

o.f = independent;

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

同樣,this的綁定只受最靠近的成員引用的影響。在下面的這個(gè)例子中,我們把一個(gè)方法g當(dāng)作對象o.b的函數(shù)調(diào)用。在這次執(zhí)行期間,函數(shù)中的this將指向o.b。事實(shí)證明,這與他是對象 o的成員沒有多大關(guān)系,最靠近的引用才是最重要的。

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

原型鏈中的this

對于在對象原型鏈上某處定義的方法,同樣的概念也適用。如果該方法存在于一個(gè)對象的原型鏈上,那么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ù)

當(dāng)一個(gè)函數(shù)用作構(gòu)造函數(shù)時(shí)(使用new關(guān)鍵字),它的this被綁定到正在構(gòu)造的新對象。

雖然構(gòu)造器返回的默認(rèn)值是this所指的那個(gè)對象,但它仍可以手動返回其他的對象(如果返回值不是一個(gè)對象,則返回this對象)。

/*
 * 構(gòu)造函數(shù)這樣工作:
 *
 * function MyConstructor(){
 *   // 函數(shù)實(shí)體寫在這里
 *   // 根據(jù)需要在this上創(chuàng)建屬性,然后賦值給它們,比如:
 *   this.fum = "nom";
 *   // 等等...
 *
 *   // 如果函數(shù)具有返回對象的return語句,
 *   // 則該對象將是 new 表達(dá)式的結(jié)果。 
 *   // 否則,表達(dá)式的結(jié)果是當(dāng)前綁定到 this 的對象。
 *   //(即通??吹降某R娗闆r)。
 * }
 */

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

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


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

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

在剛剛的例子中(C2),因?yàn)樵谡{(diào)用構(gòu)造函數(shù)的過程中,手動的設(shè)置了返回對象,與this綁定的默認(rèn)對象被丟棄了。(這基本上使得語句 “this.a = 37;”成了“僵尸”代碼,實(shí)際上并不是真正的“僵尸”,這條語句執(zhí)行了,但是對于外部沒有任何影響,因此完全可以忽略它)。

作為一個(gè)DOM事件處理函數(shù)

當(dāng)函數(shù)被用作事件處理函數(shù)時(shí),它的this指向觸發(fā)事件的元素(一些瀏覽器在使用非addEventListener的函數(shù)動態(tài)添加監(jiān)聽函數(shù)時(shí)不遵守這個(gè)約定)。

// 被調(diào)用時(shí),將關(guān)聯(lián)的元素變成藍(lán)色
function bluify(e){
    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);
}

作為一個(gè)內(nèi)聯(lián)事件處理函數(shù)

  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
<button onclick="console.log(this)"></button> // button
<button onclick="(function() { console.log(this) })()"></button> // window
<button onclick="(function() {'use strict'; console.log(this) })()"></button> // undefined

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定義:箭頭函數(shù)的this是在定義函數(shù)時(shí)綁定的,不是在執(zhí)行過程中綁定的。簡單的說,函數(shù)在定義時(shí),this就繼承了定義函數(shù)的對象。
一句話,箭頭函數(shù)內(nèi)的this就是箭頭函數(shù)外的那個(gè)this為什么?因?yàn)榧^函數(shù)沒有自己的this。

所以,這會很好的解決匿名函數(shù)和setTimeout和setInterval的this指向問題。我們不用再去給其用that變量存儲this。

// demo1
var obj = {
    func: function () {
        setTimeout(function () {
            console.log(this) // window
        }, 100)
        // console.log(this)
    },
    say:function () {
        setTimeout(() => {
          console.log(this) // obj
        }, 1000)
    }
  }
obj.func()
obj.say()
// demo2
var a = 0
var obj =  {
  a: 1,
  b1: this.a,
  b2: () => this.a,
};

console.log(obj.b1);  // 0
console.log(obj.b2());  // 0

根據(jù)結(jié)果可以看到obj對象中的屬性b1的值來自全局變量a,而不是obj對象的屬性a,也就是說b1: this.a,中的this指向window。而b2: () => this.a,中的this同樣指向window,返回全局變量a的值。

// demo3
var a = 0;
function fn(){
    var a = 2;
    console.log(this.a);  // 0
    const subf = ()=>this.a;
    console.log(subf()); // 0
}
fn()
/*
箭頭函數(shù)中有this就當(dāng)做這個(gè)箭頭函數(shù)不存在
fn函數(shù) -> window.fn()
*/
// demo4
var a = 0
var obj2 =  {
    a: 3,
    b1: function(){ return this.a;},
    b2: function(){ return ()=>this.a},
};
console.log(obj2.b1());  // 3
console.log(obj2.b2()());  // 3
/*
箭頭函數(shù)中有this就當(dāng)做這個(gè)箭頭函數(shù)不存在
this.a 指向的事當(dāng)前的對象,因此結(jié)果都是:3
*/
// demo6
var obj3 =  {
  a: 4,
  b1: function(){ return this.a;},
  b2: function(){ return ()=> () => () => this.a},
};
console.log(obj4.b1());  // 4
console.log(obj4.b2()()()());  // 4
/*
這里箭頭函數(shù)返回箭頭函數(shù)再返回箭頭函數(shù),最后一個(gè)箭頭函數(shù)返回this.a。這個(gè)this是誰?就是最后箭頭函數(shù)外面的外面的外面的那個(gè)this,也就是obj3.b1中的那個(gè)this,也就是obj3.a。:)  
實(shí)情是這樣:箭頭函數(shù)沒有自己的this,所以不管嵌套多少層,都不影響this是誰。
*/
// demo7
var a = 0
function f0() {
    var a = 5;
    setTimeout(function() {
        var b1 = this.a;
        console.log(b1);  // 0
    }, 100);
    setTimeout(function() {
        var b2 = ()=> this.a;
        console.log(b2());  // 0
    }, 200);
}

f0(); // 0  0
/*
setTimeout中的function運(yùn)行于全局環(huán)境下,所以第一個(gè)就是:0
第二個(gè)setTimeout中的函數(shù)是箭頭函數(shù),箭頭函數(shù)里面的this和setTimeout所處環(huán)境一直,因此指向的也是window下的a結(jié)果就是0

*/

更多干貨請點(diǎn)擊 - this指向完整版

同學(xué)們聽我說
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容