一樣的 this,在 Anonymous Function 與 Arrow Function 大大不同

一般人印象,Arrow Function 只是 Anonymous Function 的 syntax sugur,讓我們用更精簡的語法來表達這類只使用一次的 function,但對於 this 的認知, 兩種表示法卻有很大的差別。

Version


TypeScript 2.0.7

this 在 Anonymous Function


在 ES5,class 是使用 function 來表示,我們先使用 ES5 的方式。

this_anonymous_function.ts1 1GitHub Commit : this 在 anonymous function

this_anonymous_function.ts
1
2
3
4
5
6
7
8
9
10
11
module MyModule {
export function Foo(name: string) {
this.name = name;
this.say = function () {
console.log(`Hello, ${this.name}`);
}
}
}

var foo = new MyModule.Foo('Sam');
setTimeout(foo.say, 1000);

第 4 行

1
2
3
this.say = function () {
console.log(`Hello, ${this.name}`);
}

使用 anonymous function 來定義 say(),並在 anonymous function 使用 this

10 行

1
2
var foo = new MyModule.Foo('Sam');
setTimeout(foo.say, 1000);

建立 foo 物件,並將 foo.say 傳入 setTimeout()

實際執行,會發現 this.nameundefined,因為 JavaScript 的特性,foo.say 會變成 setTimeout 物件的一部分,但 setTimeout 並沒有定義 name,因此會 undefined,這符合我們之前對 JavaScript 對 this 的認知,與一般物件導向語言的 this 並不相同,當然也與 PHP 的 $this 不相同。

this 在 Arrow Function


ES6 提供了 arrow function,大凡本來使用 anonymous function 的地方,都可以使用 arrow function 取代。

this_arrow_function.ts2 2GitHub Commit : this 在 arrow function

this_arrow_function.ts
1
2
3
4
5
6
7
8
9
10
11
module MyModule {
export function Foo(name: string) {
this.name = name;
this.say = () => {
console.log(`Hello, ${this.name}`);
}
}
}

var foo = new MyModule.Foo('Sam');
setTimeout(foo.say, 1000);

第 4 行

1
2
3
this.say = () => {
console.log(`Hello, ${this.name}`);
}

將 anonymous function 改用 arrow function,其他都不變,一樣使用 this

實際執行,竟然出現 Hello, Sam,不再是 undefined,跟原來 anonymous function 結果不一樣!

去看編譯過的 js 檔,會發現一開始先 var _this = this

this.say 所執行的並不是 this,而是 _this

也就是 ES6 的 arrow function,其 this 跟 ES5 的 anonymous function 不同,反而跟主流物件導向語言的 this 相同,當然也跟 PHP 的 $this 相同。

this 在 Class Function


ES6 多了 class 關鍵字,當然要使用 class 可讀性較高。

this_class_function.ts3 3GitHub Commit : this 在 class function

this_class_function.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module MyModule {
export class Foo {
private name: string;

constructor(name: string) {
this.name = name;
}

public say() {
console.log(`Hello, ${this.name}`)
};
}
}

var foo = new MyModule.Foo('Sam');
setTimeout(foo.say, 1000);

第 9 行

1
2
3
public say() {
console.log(`Hello, ${this.name}`)
};

使用了我們熟悉的一般物件導向語言方式定義 say(),一樣使用 this

實際執行,又出現 undefined,也就是雖然是 ES6,this 並不是全部跟主流程式語言一樣,這裡仍維持 ES5 的 this 觀念。

this 在 Class 內使用 Arrow Function


若在 ES6 的 class 使用 arrow function,又會變成怎樣呢?我們繼續看下去:

this_class_arrow_function1.ts4 4GitHub Commit : this 在 class 內使用 arrow function 第一種方法

this_class_arrow_function1.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module MyModule {
export class Foo {
private name: string;

constructor(name: string) {
this.name = name;
}

public say: ()=>void = ()=> {
console.log(`Hello, ${this.name}`)
};
}
}

var foo = new MyModule.Foo('Sam');
setTimeout(foo.say, 1000);

第 9 行

1
2
3
public say: ()=>void = ()=> {
console.log(`Hello, ${this.name}`)
};

由於 TypeScript 是強行別語言,所以需要定義 say 的型別為 ()=>void 這種 arrow function 型別,接著才能定義 arrow function,一樣使用 this

這樣就不會 undefined 了。

還有另外一種寫法,讓我們在 constructor 去定義 arrow function。

this_class_arrow_function2.ts5 5GitHub Commit : this 在 class 內使用 arrow function 第二種方法

this_class_arrow_function2.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module MyModule {
export class Foo {
private name: string;
public say: ()=>void;

constructor(name: string) {
this.name = name;
this.say = () => {
console.log(`Hello, ${this.name}`);
};
}
}
}

var foo = new MyModule.Foo('Sam');
setTimeout(foo.say, 1000);

第 4 行

1
public say: ()=>void;

先在 field 內定義好 say 的型別為 arrow function。

第 6 行

1
2
3
4
5
6
constructor(name: string) {
this.name = name;
this.say = () => {
console.log(`Hello, ${this.name}`);
};
}

在 constructor 內去定義 arrow function,雖然可行,不過實務上不建議這樣寫,除非 method 真的程式碼很短,否則 constructor 會非常的大而難以維護。

實際執行,也不再 undefined

Conclusion


  • 無論 ES5 或 ES6,this 都維持 JavaScript 一貫的想法,也就是當 function 內掛在哪一個物件,this 就是那個物件。
  • 唯獨 arrow function 例外,arrow function 會事先將 _this = this,而之後都使用 _this,這種思維與一般物件導向語言相同,但跟傳統 JavaScript 不同。
  • Anonymous function 與 arrow function 對於 this 的看法都有他的道理,要視狀況使用,大部分狀況,arrow function 與一般物件導向語言的思維一樣,但有些應用,anonymous function 的想法比較方便。

Sample Code


完整的範例可以在我的 GitHub 上找到。

2016-11-05