TypeScript 中的 this指向問題
TypeScript 中的 this指向問題
如果你接觸過TypeScript有一段時間,那麽你會發現很多並非語言設計當中的“特征”。這些所謂的特征並非真正TypeScript的語法糖部分,也並非語言設計當中絕妙的點子。仔細研究TypeScript代碼與編譯後的 JavaScript 代碼,你會發現,很多東西其實都是委曲求全的產物。
這篇文章裏,我們僅來討論關於TypeScript中 this 作用域的問題,你應該透過我的描述來了解如何去理解問題的思路。
我們來一段 TypeScript 代碼:
class demo { private str:string = "hello"; public abc() { console.log( this.str ); } }
執行 tsc 編譯器,在同目錄中生成一個a.js文件,內容如下:
var demo = (function () {
function demo() {
this.str = "hello";
}
demo.prototype.abc = function () {
console.log(this.str);
};
return demo;
})();
生成的JS代碼並不難理解,還是先回過頭來看 TypeScript 代碼。
當我們創建一個類,並在類中定義了一個私有屬性,在 TypeScript 中的解釋為,名稱為 str 的屬性作用域在類中。 str 與 方法 abc 所在同一域中,並且屬於平行關系。記住,這種解釋是在 TypeScript 中存在的。
我們簡單修改一下生成後的 JavaScript 代碼,新代碼內容如下:
var demo = (function () {
function demo() {
this.str = "hello";
}
demo.prototype.abc = function () {
console.log(this.str);
};
return demo;
})();
var d = new demo();
d.abc();
最後兩行是我手動添加的,在nodejs中運行,可以看到打印出 “hello” 字符。
現在已經確保我們的代碼沒有任何問題,可以正常執行。
在 TypeScript 中,我們看到 abc 方法在訪問 str 屬性時候添加了 this. 。在其他編程語言中,通常函數內的調用都會先從函數域開始檢索,然後尋找上一層域中的定義。
但如果你在 TypeScript 中這麽做了,就無法找到上一層的 str 定義。在編譯過程中,你將得到一個報錯信息。
TypeScript 這麽做的目的是為了防止域指向錯誤,在得到的翻譯後的JS代碼中,依然保留相同語句,已防止錯誤的發生。
在來看另外一種情況,回調函數。看一下修改後的JS代碼:
var demo = (function () {
function demo() {
this.str = "hello";
}
demo.prototype.abc = function () {
console.log( this.str);
};
demo.prototype.doing = function(callback){
callback();
}
return demo;
})();
var d = new demo();
d.abc();
d.doing(d.abc);
運行後得到什麽結果?
會先輸出一個 hello
在輸出一個 undefined
。恭喜你,你已經遇到了JS當中最惡心人的作用域問題了。
在JS中,如何避免這種問題呢?一般是將 this 賦值給另外一個變量,通過這種方式來避免作用域問題。
修改代碼如下:
var demo = (function () {
var that;
function demo() {
this.str = "hello";
that = this;
}
demo.prototype.abc = function () {
console.log( that.str);
};
demo.prototype.doing = function(callback){
callback();
}
return demo;
})();
var d = new demo();
d.abc();
d.doing(d.abc);
此時你能看到輸出兩個 hello
字符。到此為止你已經找到了解決方法,但是,你仍然無法通過一些巧妙的手段讓 TypeScript 翻譯成現在的樣子。
但TypeScript提供了一個另外一個變通的解決方法,來實現這個功能。
class demo
{
private str:string = "hello";
public abc()
{
console.log( this.str );
}
public doing(callback)
{
callback();
}
public start()
{
this.doing(this.abc);
}
}
這樣的TS代碼翻譯之後得到如下JS代碼:
var demo = (function () {
function demo() {
this.str = "hello";
}
demo.prototype.abc = function () {
console.log(this.str);
};
demo.prototype.doing = function (callback) {
callback();
};
demo.prototype.start = function () {
this.doing(this.abc);
};
return demo;
})();
在JS中增加兩行代碼:
var d = new demo();
d.start();
你會看到打印出了 undefined
,我們需要稍微修改一下 TypeScript 代碼。
class demo
{
private str:string = "hello";
public abc()
{
console.log( this.str );
}
public doing(callback)
{
callback();
}
public start()
{
this.doing(()=>{this.abc()});
}
}
可以看到一個非常不規範的語法,語法格式為 ()=>{}。 翻譯之後的JS代碼如下:
var demo = (function () {
function demo() {
this.str = "hello";
}
demo.prototype.abc = function () {
console.log(this.str);
};
demo.prototype.doing = function (callback) {
callback();
};
demo.prototype.start = function () {
var _this = this;
this.doing(function () { _this.abc(); });
};
return demo;
})();
不難看出 TypeScript 是將這種技巧封裝到了語言層面,並為其取名叫做 “Arrow Function”。
TypeScript 中的 this指向問題