[JS奇怪的世界]No.29 物件、函數與this

前言

這邊將會講一開始講到的執行環境,還有前面沒有講到到的this。

物件、函數與this

首先當我們執行函數物件的時候會發生什麼事情呢?我們都知道每一個執行環境下都有屬於自己的執行環境,另外它也可以參考外部環境,所以當執行環境被創造時 JavaScript 都會建立一個我們不曾宣告過的東西,那就是 this。

this 通常是很多人撞牆的點,在某些情況下 this 會依據函數如何被呼叫而改變指向。

this

這邊將會用幾個簡單範例來講 this 在不同的環境下會指向哪裡,在前面已經我們知道 this 會取用(參考) window,這是因為 this 在目前的執行環境是指向全域環境物件 ↓

1
console.log(this);

那如果今天是這樣子呢?它會指向哪裡呢?

1
2
3
4
5
function a (){
console.log(this);
}

a();

this

this 是會指向到全域物件的,那如果今天使用匿名函數呢 ↓

1
2
3
4
5
var b = function () {
console.log(this);
};

b();

this

也是一樣指向到全域物件,上面幾個範例我們可以看到我們在函數表示式、函數陳述句等環境下使用 this 都是指向 window,但是這邊要注意到一件事情,函數都有屬於自己的執行環境,所以他們都有屬於自己的 this,但是在上面的例子中 this 都是指向同一個位置 window,為什麼呢?讓我們繼續來看範例 ↓

1
2
3
4
5
6
7
function a (){
console.log(this);
this.newvalue = 'hello';
}

a();
console.log(newvalue);

newvalue

我們可以看到 newvalue 出現的是在函數 a()Hello,所以如果拆開來看詳細一點,程式碼就會長這樣子 ↓

1
2
3
4
5
6
7
function a (){
console.log(this);
window.newvalue = 'hello';
}

a();
console.log(newvalue);

其實就是在全域環境下建立變數。

物件

那如果是在物件中使用 this 呢?

1
2
3
4
5
6
7
8
var c = {
name: 'The C object',
log: function() {
console.log(this);
},
};

c.log();

物件

我們可以看到 this 所指向的不是全域物件了,而是物件的本身,當 JavaScript 發現函數連接到物件方法的時候,this 就會改指向到物件,所以我們就可以透過這種方式來取得資料 ↓

1
2
3
4
5
6
7
8
9
10
11
var c = {
name: 'The C object',
log: function() {
console.log(this.name);
console.log(this);
console.log(this.name = 'Hello C');
console.log(this.name);
},
};

c.log();

物件

所以我們也可以透過這種方式來修物件屬性 this.name = 'Hello C' (記住物件式傳參考特性)。

this 是一個很好用的東西,它可以幫助我們確保取得同一物件的方法與屬性,但是要注意不同的環境下會有不同的指向。

接下來將會呈現許多人都會認為這是一個 bug,這是錯的。

1
2
3
4
5
6
7
8
9
10
11
12
var c = {
name: 'The C object',
log: function() {
var setname = function (newname) {
this.name = newname;
}
setname('Updated again!');
console.log(this);
},
};

c.log();

bug

可以發現 setname('Updated again!'); 這一行並沒有任何動作,但是這時候打開 window

window

我們可以發現 this.name = newname; 創建到全域物件下,這邊就證明一件事情var setname = function (newname) { this.name = newname; } 的執行環境是全域物件。

那如果遇到這種狀況該怎麼確保我們取得正確的物件屬性?這時候可以在 log() 設置屬性

1
2
3
4
5
6
7
8
9
10
11
12
13
var c = {
name: 'The C object',
log: function() {
var vm = this;
var setname = function (newname) {
vm.name = newname;
}
setname('Updated again!');
console.log(vm);
},
};

c.log();

那為什麼這樣做?因為 this 是一個物件,所以這邊利用了物件傳參考特性,將它指向到同一個記憶體位子,這個好處我們就不用擔心 this 指向錯地方。

而這邊觀念也與 JavaScript 範圍鏈觀念有關係,vm 並沒有在匿名函數中建立,所以 JavaScript 就會往外部環境找,所以往外一層就會找到 var vm = this;

每個語言都有一個奇怪的地方,但我們可以從一些脈絡去找到蛛絲馬跡。

如果不清楚 this 會指向哪裡,就使用 console.log() 就可以知道了。

圖源

JavaScript 全攻略:克服 JS 奇怪的部分

0%