當函數被呼叫時執行環境會被創造,也會替我們創造變數環境
圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 4 節講座 37 影片截圖
執行環境也會替我們創造外部參考環境(外部詞彙環境),
方便在我們找尋一個在函數中不存在的變數或函數時,
會往範圍鏈裡面去找,直到全域執行環境為止,
圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 4 節講座 37 影片截圖
除此之外執行環境創造時還會幫我們創造特殊變數 this ,
this 會指向不同物件
this 會依據我們在哪裡呼叫而有所不同,
圖片來源:JavaScript 全攻略:克服 JS 的奇怪部分課程第 4 節講座 37 影片截圖
我們來看些範例,
程式碼如下:
console.log(this);
我們都知道如果我們在全域環境呼叫 this 會指向全域物件,
我們到 Chrome 的 Console 來看:
這在我們的預料之內,但我們接下來看看其他範例,
接著我們看函數,
先使用函數陳述句來新增函數,
程式碼如下:
function a() {
console.log(this);
}
a();
我們呼叫函數 a 時,函數 a 的執行環境背創造同時也創造特殊變數 this ,
接著執行程式屬性(函數中的所有程式碼),
來看 Console 中的結果:
可以看到在全域環境創造的函數的 this 會指向全域物件 Window,
如果換成函數表達式呢?
我們一樣在全域環境下新增一個函數
程式碼如下:
var b = function() {
console.log(this);
}
b();
函數現在被儲存到變數 b 的記憶體位址裡,
那我們呼叫它來看 this 的結果:
可以看到我們在全域環境下不管用函數陳述句或函數表達式,
當函數被呼叫時,this 都是指向到全域物件(瀏覽器的全域物件是 Window),
在全域環境創建的函數的 this 沒有指向函數本身,
我在函數中透過 this 與點(成員取用)運算子來新增屬性到全域物件 Window ,
程式碼如下:
function a() {
console.log(this);
this.newvariable = 'Hi';
}
a();
console.log(newvariable);
我們到 Console 中看結果:
我們不用透過 this 與點(成員取用)運算子就能直接取用到全域物件底下的 newvariable 屬性,
在物件中的函數我們稱為方法,
現在來看看這個例子,
程式碼如下:
var c = {
name: 'The c object ',
log: function() {
console.log(this);
}
}
c.log();
我們到 Console 中看結果:
現在物件中的方法(函數),被呼叫時執行環境被創造,同時創造特殊變數 this ,
在這個物件的方法裡的 this 指向物件(變數 c)本身,
在物件中可以透過 this 修改屬性或方法,
我們修改一下剛才的程式碼,
程式碼如下:
var c = {
name: 'The c object ',
log: function() {
this.name = "Updated c object",
console.log(this);
}
}
c.log();
這時屬性 name 會被修改成新的內容,
Console 中的結果:
在物件的方法中可以透過 this 來取用物件的屬性與方法,
如果方法中又有其他函數情況會不太一樣,
程式碼如下:
var c = {
name: 'The c object ',
log: function() {
this.name = "Updated c object";
var setName = function (newName) {
this.name = newName;
}
setName('Updated again c object');
console.log(this);
}
}
c.log();
到 Console 中來看結果:
會發現物件方法中的函數 setName 並沒有修改到物件的 name 屬性,
屬性的值還是跟剛才一樣,
這時可以從全域物件 Window 中找到,
如下圖:
我們可以在 Console 中 輸入 window 來查看,會發現 name 屬性被新增到全域物件 Window 底下
物件方法中的函數的 this 跟我們想像的不一樣,他不是指向物件本身,而是全域物件 Window,
要解決這個情況可以使用另一種方式,
程式碼如下:
var c = {
name: 'The c object ',
log: function() {
var self = this;
self.name = "Updated c object";
var setName = function (newName) {
self.name = newName;
}
setName('Updated again c object');
console.log(this);
}
}
c.log();
我們在物件的 log 方法第1行使用一個變數 self 來儲存 this ,
這會讓 self 與 this 指向同一個記憶體位址,我們知道物件是 by reference 的,
現在在方法裡與方法中的函數裡都改用 self 變數來取用屬性,
我們到 Console 中看是否如我們的預期 ,
Console 中的結果如下:
透過這個方式物件的 log 方法中的子函數也可以取用到物件裡的屬性了,因為變數 self 與物件的 this 現在指向同一個記憶體位置,
現在我們知道呼叫函數時也會創造特殊變數 this ,
透過 this 來修改屬性值是非常長用到的技巧,
不過要小心你的 this 是不是你要的 this。