1. 程式人生 > >TypeScript 中的 this指向問題

TypeScript 中的 this指向問題

編程 依然 5-0 簡單 下午 cal call 調用 src

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指向問題