普通函數(shù)的this指向
簡單說說
首先霉祸,按照慣例,我們先舉個栗子:
var bar = 2;
function foo() {
this.bar = 1;
this.getBar = function() {
console.log(this.bar);
}
}
var test = new foo();
var getBar = test.getBar;
test.getBar(); //1
getBar(); //2
通過這個例子我們就能看到袱蜡,雖然是同一個函數(shù)丝蹭,但是實際上得到的結(jié)果卻不一樣。這個原因相信大家都能知道坪蚁。不知道的也告訴你:this其實是指向調(diào)用該函數(shù)的那個對象奔穿。那么當(dāng)我們在全局環(huán)境中調(diào)用的時候,this自然就指向了全局環(huán)境敏晤。
那么到是有個問題:this為什么會隨調(diào)用者變化而變化贱田?
這可能需要你繼續(xù)往下看看
深入說說
那么如果說深層次的理解this的指向,我覺得大概可以從數(shù)據(jù)類型講起
我們都知道茵典,棧中存放的是基本數(shù)據(jù)類型湘换,也就是String
、Number
统阿、Boolean
彩倚、Symbol
、Null
扶平、Undefined
這七種數(shù)據(jù)類型帆离,當(dāng)然Symbol
是ES6新增的一個數(shù)據(jù)類型。那么堆中存放的就是一些引用類型了结澄,如Obejct
哥谷、Function
。實際上當(dāng)我們定義一個引用類型的時候麻献,js會同時定義一個地址指針指向內(nèi)存中的對象们妥。
例如:當(dāng)我們聲明一個字面量對象時候let a = {num:1};
實際上a
中存放的是指向{num:1}
的地址
現(xiàn)在我們解析一下上面那段代碼是如何執(zhí)行的
// 在全局環(huán)境下定義一個變量bar
var bar = 2;
function foo() {
//在foo中也聲明了一個bar
this.bar = 1;
//在foo中聲明一個getBar函數(shù)
this.getBar = function() {
console.log(this.bar);
}
}
//構(gòu)造函數(shù)模式自定義對象,將foo的this賦予test
var test = new foo();
//將test中的getBar方法賦予getBar
var getBar = test.getBar;
//調(diào)用test中的getBar
test.getBar(); //1
//調(diào)用getBar
getBar(); //2
現(xiàn)在列出來一看勉吻,放佛恍然大悟监婶,終于知道為啥輸出的是不同的結(jié)果了。那么我這里倒是有幾個問題
- 為什么調(diào)用同一個函數(shù)卻有不同的結(jié)果?
- foo中的this是指向foo的惑惶,為什么foo中的函數(shù)可以取得外部的this煮盼?
- 為什么this會隨調(diào)用它的對象變化而變化?
ok带污,其實要弄清楚上述問題僵控,我們需要明白一點,函數(shù)也是個引用類型鱼冀。那么我們上面講過报破,創(chuàng)建引用類型的時候會同時創(chuàng)建一個地址指針。那么我們就可以這樣理解上面的foo對象
實際上foo中的getBar只是存放了一個函數(shù)的地址而已*雷绢。那么這個函數(shù)并不是foo所私有泛烙。什么東西是foo的呢?一個值為1的bar和一個指向function() {console.log(this.bar);}
函數(shù)的getBar而已翘紊。
這樣我們就不難理解蔽氨,為什么調(diào)用同一個函數(shù)會有不一樣的結(jié)果了,因為這個函數(shù)并不是foo
所私有帆疟。好比內(nèi)存就是深圳鹉究,函數(shù)就只是深圳的一套房。getBar
就是這套房的鑰匙踪宠。那么一開始foo
這個人建好了這房子自赔,就他有這房子的鑰匙,那么當(dāng)然只有他能進出該房子柳琢,后來有一天他把鑰匙多配了一把給了window
這好朋友绍妨。于是乎window
也能進這套房了。給window
配鑰匙的過程:var getBar = test.getBar;
這里只是將該函數(shù)的地址賦給全局下的getBar而已柬脸,房子也只是一套房子他去,函數(shù)還是一個函數(shù)。
由于函數(shù)可以在不同的運行環(huán)境執(zhí)行倒堕,所以需要有一種機制灾测,能夠在函數(shù)體內(nèi)部獲得當(dāng)前的運行環(huán)境(context)。所以垦巴,this就出現(xiàn)了媳搪,它的設(shè)計目的就是在函數(shù)體內(nèi)部,指代函數(shù)當(dāng)前的運行環(huán)境骤宣。
所以當(dāng)window調(diào)用這個函數(shù)的時候秦爆,this就不是指向foo了。而是指向window憔披。this是指向他們自己等限。window的衣服不會在進了foo的房子以后就變成foo的衣服。
ok,我們現(xiàn)在再把剛剛的代碼重新注釋一下
// 在全局環(huán)境下定義一個變量bar
var bar = 2;
function foo() {
//在foo中也聲明了一個bar
this.bar = 1;
//在foo中聲明一個getBar函數(shù)精刷,getBar存放該函數(shù)的地址
this.getBar = function() {
console.log(this.bar);
}
}
//構(gòu)造函數(shù)模式自定義對象,將foo的this賦予test
var test = new foo();
//將test中的getBar方法的地址賦予全局的getBar
var getBar = test.getBar;
//調(diào)用test中的getBar函數(shù)
test.getBar(); //1
//調(diào)用getBar函數(shù)
getBar(); //2
于是乎我們就把普通的this指向弄明白了蔗候。順便還明白了堆棧的區(qū)別怒允。接下來看看不普通的函數(shù)this指向是如何的
箭頭函數(shù)this指向
箭頭函數(shù)內(nèi)沒有this,箭頭函數(shù)的this是父級函數(shù)的this
// 在全局環(huán)境下定義一個變量bar
var bar = 2;
function foo() {
//在foo中也聲明了一個bar
this.bar = 1;
//在foo中定義一個箭頭函數(shù)锈遥,getBar存放該函數(shù)的地址
this.getBar = () => {
console.log(this.bar);
}
}
//構(gòu)造函數(shù)模式自定義對象纫事,將foo的this賦予test
var test = new foo();
//將test中的getBar方法的地址賦予全局的getBar
var getBar = test.getBar;
//調(diào)用test中的getBar函數(shù)
test.getBar(); //1
//調(diào)用getBar函數(shù)
getBar(); //1
如果定義了箭頭函數(shù)的情況下,this執(zhí)行就不會隨意的改變了所灸。普通函數(shù)的this是會跟隨調(diào)用者變化丽惶,但是箭頭函數(shù)就很特別,他只會繼承父級的this爬立,而且一旦建立就不會改變了钾唬。所以在這里我們就可以看見,盡管全局下面調(diào)用getBar侠驯,但是實際上還是取到了foo的this抡秆。
因此箭頭函數(shù)不可以用來當(dāng)作構(gòu)造函數(shù)。因為它本身是沒有this的吟策!
所以箭頭函數(shù)使用的話需要與普通函數(shù)區(qū)別開這點儒士,它的this指向定義函數(shù)時候的父級。
后話
關(guān)于this就介紹到這里檩坚,如果有什么不懂的歡迎隨時提問着撩,我會隨時回答大家的問題。
那么最后匾委,成功不在一朝一夕拖叙,我們都需要努力