首先,按照慣例,咱們先舉個栗子:函數
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
經過這個例子咱們就能看到,雖然是同一個函數,可是實際上獲得的結果卻不同。這個緣由相信你們都能知道。不知道的也告訴你:this實際上是指向調用該函數的那個對象。那麼當咱們在全局環境中調用的時候,this天然就指向了全局環境。this
那麼到是有個問題:this爲何會隨調用者變化而變化?spa
這可能須要你繼續往下看看設計
那麼若是說深層次的理解this的指向,我以爲大概能夠從數據類型講起指針
咱們都知道,棧中存放的是基本數據類型,也就是String
、Number
、Boolean
、Symbol
、Null
、Undefined
這七種數據類型,固然Symbol
是ES6新增的一個數據類型。那麼堆中存放的就是一些引用類型了,如Obejct
、Function
。實際上當咱們定義一個引用類型的時候,js會同時定義一個地址指針指向內存中的對象。code
例如:當咱們聲明一個字面量對象時候let a = {num:1};
實際上a
中存放的是指向{num:1}
的地址對象
如今咱們解析一下上面那段代碼是如何執行的繼承
// 在全局環境下定義一個變量bar var bar = 2; function foo() { //在foo中也聲明瞭一個bar this.bar = 1; //在foo中聲明一個getBar函數 this.getBar = function() { console.log(this.bar); } } //構造函數模式自定義對象,將foo的this賦予test var test = new foo(); //將test中的getBar方法賦予getBar var getBar = test.getBar; //調用test中的getBar test.getBar(); //1 //調用getBar getBar(); //2
如今列出來一看,放佛恍然大悟,終於知道爲啥輸出的是不一樣的結果了。那麼我這裏卻是有幾個問題內存
ok,其實要弄清楚上述問題,咱們須要明白一點,函數也是個引用類型。那麼咱們上面講過,建立引用類型的時候會同時建立一個地址指針。那麼咱們就能夠這樣理解上面的foo對象rem
實際上foo中的getBar只是存放了一個函數的地址而已*。那麼這個函數並非foo所私有。什麼東西是foo的呢?一個值爲1的bar和一個指向function() {console.log(this.bar);}
函數的getBar而已。
這樣咱們就不難理解,爲何調用同一個函數會有不同的結果了,由於這個函數並非foo
所私有。比如內存就是深圳,函數就只是深圳的一套房。getBar
就是這套房的鑰匙。那麼一開始foo
這我的建好了這房子,就他有這房子的鑰匙,那麼固然只有他能進出該房子,後來有一天他把鑰匙多配了一把給了window
這好朋友。因而乎window
也能進這套房了。給window
配鑰匙的過程:var getBar = test.getBar;
這裏只是將該函數的地址賦給全局下的getBar而已,房子也只是一套房子,函數仍是一個函數。
因爲函數能夠在不一樣的運行環境執行,因此須要有一種機制,可以在函數體內部得到當前的運行環境(context)。因此,this就出現了,它的設計目的就是在函數體內部,指代函數當前的運行環境。
因此當window調用這個函數的時候,this就不是指向foo了。而是指向window。this是指向他們本身。window的衣服不會在進了foo的房子之後就變成foo的衣服。
ok,咱們如今再把剛剛的代碼從新註釋一下
// 在全局環境下定義一個變量bar var bar = 2; function foo() { //在foo中也聲明瞭一個bar this.bar = 1; //在foo中聲明一個getBar函數,getBar存放該函數的地址 this.getBar = function() { console.log(this.bar); } } //構造函數模式自定義對象,將foo的this賦予test var test = new foo(); //將test中的getBar方法的地址賦予全局的getBar var getBar = test.getBar; //調用test中的getBar函數 test.getBar(); //1 //調用getBar函數 getBar(); //2
因而乎咱們就把普通的this指向弄明白了。順便還明白了堆棧的區別。接下來看看不普通的函數this指向是如何的
箭頭函數內沒有this,箭頭函數的this是父級函數的this
// 在全局環境下定義一個變量bar var bar = 2; function foo() { //在foo中也聲明瞭一個bar this.bar = 1; //在foo中定義一個箭頭函數,getBar存放該函數的地址 this.getBar = () => { console.log(this.bar); } } //構造函數模式自定義對象,將foo的this賦予test var test = new foo(); //將test中的getBar方法的地址賦予全局的getBar var getBar = test.getBar; //調用test中的getBar函數 test.getBar(); //1 //調用getBar函數 getBar(); //1
若是定義了箭頭函數的狀況下,this執行就不會隨意的改變了。普通函數的this是會跟隨調用者變化,可是箭頭函數就很特別,他只會繼承父級的this,並且一旦創建就不會改變了。因此在這裏咱們就能夠看見,儘管全局下面調用getBar,可是實際上仍是取到了foo的this。
所以箭頭函數不能夠用來看成構造函數。由於它自己是沒有this的!
因此箭頭函數使用的話須要與普通函數區別開這點,它的this指向定義函數時候的父級。
關於this就介紹到這裏,若是有什麼不懂的歡迎隨時提問,我會隨時回答你們的問題。
那麼最後,成功不在一朝一夕,咱們都須要努力