JavaScript當中的this究竟是個啥?

lcc發表於2021-09-09

對於JS的初學者而言,JS當中的this指向很難讓人摸準其脈絡,經常會給你一種模糊美、朦朧美的感腳!因為this並不是固定不變的,它會根據自身所執行的環境的不同而不同。而且在開發的過程中,經常因為對this的不瞭解出現這樣或那樣的錯誤!所以搞定this的指向是非常非常有必要的!

與其它語言當中的this不同的是,我們JS當中的this總是指向一個物件。而具體是指向哪一個物件,則要看其執行時是基於哪一個函式的執行環境所動態繫結的。

注意:this的指向並不是函式被宣告時的環境。

具體到實際開發中,this的指向大致可以分為以下幾種:

1、作為普通函式呼叫

當你的函式不是作為物件的屬性來呼叫時,即是我們經常說的普通函式呼叫。此時的this為全域性物件,而JS當中的全域性物件指的是window。
作為普通物件呼叫:

//定義一個全域性變數agevar age=18;//宣告一個全域性函式getAgefunction getAge(){    return this.age;
}//因為是全域性環境內呼叫的getAget函式所以指向的物件為windowconsole.log(getAge());//18//你也可以這樣寫console.log(window.getAge());//18

以上程式碼中,getAge方法在全域性window下呼叫,所以getAge方法內的this指向的是window。為了更好的驗證這一點,我們們再來對以上程式碼修改如下:

//宣告一個全域性函式getAgefunction getAge(){    //由於該函式在全域性環境(window)下呼叫,所以this為window
    this.age=81;//為this指向的window物件新增屬性age}
getAge();//全域性呼叫函式getAge()console.log(age);//81//也可以這樣輸出console.log(window.age);//81

以上程式碼中宣告瞭一個全域性函式getAge。由於該函式在全域性環境(window)下呼叫,所以this為window。然後透過this.age為window物件增加一個age屬性。所以在呼叫完該函式後進行console.log(window.age)輸出的結果為81。

2、函式作為物件的屬性來呼叫:

如果函式作為物件的屬性來呼叫,函式內的this為呼叫函式的物件。

var obj={    //obj屬性age
    age:12,    //obj方法getAge
    getAge:function(){        return this.age;
    }
}//全域性屬性agevar age=13;//全域性方法getAgefunction getAge(){    return this.age;
}//在obj物件下呼叫getAge(),this代表的是objconsole.log(obj.getAge());//12

在全域性環境下,this代表的是window。所以調取的方法為全域性方法getAge。又因為getAge是在window下呼叫的,所以內部this指向的是window物件。最終輸出結果為13

var obj={    //obj屬性age
    age:12,    //obj方法getAge
    getAge:function(){        return this.age;
    }
}//全域性屬性agevar age=13;//全域性方法getAgefunction getAge(){    return this.age;
}//在全域性環境下,this代表window,所以下面可以理解為window物件下呼叫全域性getAgeconsole.log(this.getAge());//13

接下來看種丟失掉this的情況,換言之,this的指向發生改變。我們先來看下面的程式碼

var obj={    //obj屬性age
    age:12,    //obj方法getAge
    getAge:function(){        return this.age;
    }
}//全域性屬性agevar age=13;//全域性方法getAgefunction getAge(){    return this.age;
}//在obj物件下呼叫getAge(),this代表的是objconsole.log(obj.getAge());//12//將obj下的函式getAge賦值給fn。var fn=obj.getAge;//在全域性環境(window) 下呼叫fn,this代表的是windowconsole.log(fn());//13

當呼叫obj.getAge時,getAge方法是作為obj物件的屬性來呼叫的。輸出結果為12。當將obj.getAge賦值給一個變數fn時,因為fn的呼叫是在全域性環境下呼叫的,所以this指向的是window,輸出結果為13。
將程式碼彙總如下,認真看看:

var obj={    //obj屬性age
    age:12,    //obj方法getAge
    getAge:function(){        return this.age;
    }
}//全域性屬性agevar age=13;//全域性方法getAgefunction getAge(){    return this.age;
}//在obj物件下呼叫getAge(),this代表的是objconsole.log(obj.getAge());//12//在全域性環境下,this代表window,所以下面可以理解為window物件下呼叫全域性getAgeconsole.log(this.getAge());//13//將obj下的函式getAge賦值給fn。var fn=obj.getAge;//在全域性環境(window) 下呼叫fn,this代表的是windowconsole.log(fn());//13
3、DOM物件的事件函式

DOM物件的事件函式內的this指向的是該DOM物件,看以下程式碼:

    
點我吧!

在事件函式內新增一個子函式_fn,該子函式內的this指向的是window:

    
點我吧!

但往往我們需要的是讓它指向觸發事件的DOM物件,此時有一種解決方法可以作為參考:

    
點我吧!
4、建構函式

在JS當中並沒有類的概念,但是我們可以透過建構函式來建立物件,而且JS也提供了new運算子,使建構函式看起來更像是一個類!
建構函式與普通函式的異同:建構函式與普通函式的個表是一樣的,它們的區別在於調取的方式。當用new運算子呼叫函式時,該函式為建構函式,否則為普通函式。

function Box(){    this.age=14;
}var obj=new Box();console.log(obj.age);//14

透過new來呼叫建構函式時的執行流程如下:

  • new 建構函式(),隱式執行了new Object();

  • 將建構函式的作用域給新物件(即new Object()建立出的物件),函式體內的this就代表這個新物件。

  • 執行建構函式的語句。

  • 隱式直接返回新物件。
    需要注意的是建構函式如果直接返回一個物件,那麼執行返回的物件就不是我們所期待的this:

function Box(){    this.age=14;    return {        age:16
    }
}var obj=new Box();console.log(obj.age);//16
5、透過call與apply改變this的指向

透過call與apply可以動態的改變函式內的this

//宣告一個全域性變數colorvar color="red";//宣告一個全域性物件obj,為 obj新增一個屬性color值為yellowvar obj= {    color: "yellow"}//新增一個建構函式function Fn(){    this.color="blue";
}//普通函式function getColor(){    console.log(this.color);
}
getColor();//redgetColor.call(window);//redgetColor.call(this);//redgetColor.call(obj);//yellowgetColor.call(new Fn());//blue

以上程式碼透過call來改變函式體內this的指向,以上程式碼用apply也是可以實現相同的功能。只需要將call改變apply即可:

var color="red";var obj= {    color: "yellow"}function Fn(){    this.color="blue";
}function getColor(){    console.log(this.color);
}
getColor();//redgetColor.apply(window);//redgetColor.apply(this);//redgetColor.apply(obj);//yellowgetColor.apply(new Fn());//blue

call與apply的用法是一樣,區別僅在於傳入的引數形式不同。

var num=5;function fn(num1,num2){    console.log(num1+num2+this.num);
}var obj={    num:8}
fn.call(this,1,2);//8fn.call(obj,1,2);//11//apply 傳遞的引數需要用中括號進行包裹fn.apply(this,[3,4]);//12fn.apply(obj,[3,4]);//12

6、透過bind指定函式內部的this

var age=81;var obj={    age:18}//指定fn函式體內的this指向windowvar fn=function(){    console.log(this.age);
}.bind(this);//指定fn2函式體內的this指向objvar fn2=function(){    console.log(this.age);
}.bind(obj);
fn();//81fn2();//18

好了,今天就先到這裡吧!



作者:張培躍
連結:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2331/viewspace-2813627/,如需轉載,請註明出處,否則將追究法律責任。

相關文章