TypeScript基礎入門-變數宣告(一)

durban發表於2018-07-26

轉載地址

TypeScript基礎入門 – 變數宣告(一)

專案實踐倉庫

https://github.com/durban89/typescript_demo.git
tag: 1.0.3

為了保證後面的學習演示需要安裝下ts-node,這樣後面的每個操作都能直接執行看到輸出的結果。

npm install -D ts-node

後面自己在練習的時候可以這樣使用

npx ts-node src/learn_basic_types.ts
npx ts-node 指令碼路徑

變數宣告

let和const是JavaScript裡相對較新的變數宣告方式。 像我們之前提到過的, let在很多方面與var是相似的,但是可以幫助大家避免在JavaScript裡常見一些問題。 const是對let的一個增強,它能阻止對一個變數再次賦值。

因為TypeScript是JavaScript的超集,所以它本身就支援let和const。 下面我們會詳細說明這些新的宣告方式以及為什麼推薦使用它們來代替 var。

如果你之前使用JavaScript時沒有特別在意,那麼這節內容會喚起你的回憶。 如果你已經對 var宣告的怪異之處瞭如指掌,那麼可以瞭解加深下記憶。

var 宣告

一直以來我們都是通過var關鍵字定義JavaScript變數。

var a = 10;

大家都能理解,這裡定義了一個名為a值為10的變數。我們也可以在函式內部定義變數:

function f() {
    var message = "Hello, world!";

    return message;
}

並且我們也可以在其它函式內部訪問相同的變數。

function f() {
    var a = 10;
    return function g() {
        var b = a + 1;
        return b;
    }
}

var g = f();
console.log(g());

執行後得到的結果如下

11

上面的例子裡,g可以獲取到f函式裡定義的a變數。 每當 g被呼叫時,它都可以訪問到f裡的a變數。 即使當 g在f已經執行完後才被呼叫,它仍然可以訪問及修改a。

function f() {
    var a = 1;

    a = 2;
    var b = g();
    a = 3;

    return b;

    function g() {
        return a;
    }
}

console.log(f());

執行後得到的結果如下

2

作用域規則

對於熟悉其它語言的人來說,var宣告有些奇怪的作用域規則。 看下面的例子:

function f(shouldInitialize: boolean) {
    if (shouldInitialize) {
        var x = 10;
    }

    return x;
}

console.log(f(true));
console.log(f(false));

執行後得到的結果如下

10
undefined

有些讀者可能要多看幾遍這個例子。 變數 x是定義在*if語句裡面*,但是我們卻可以在語句的外面訪問它。 這是因為 var宣告可以在包含它的函式,模組,名稱空間或全域性作用域內部任何位置被訪問,包含它的程式碼塊對此沒有什麼影響。 有些人稱此為 *var作用域或函式作用域*。 函式引數也使用函式作用域。這些作用域規則可能會引發一些錯誤。 其中之一就是,多次宣告同一個變數並不會報錯:

function sumMatrix(matrix: number[][]) {
    var sum = 0;
    for (var i = 0; i < matrix.length; i++) {
        var currentRow = matrix[i];
        for (var i = 0; i < currentRow.length; i++) {
            sum += currentRow[i];
        }
    }

    return sum;
}

這裡很容易看出一些問題,裡層的for迴圈會覆蓋變數i,因為所有i都引用相同的函式作用域內的變數。 有經驗的開發者們很清楚,這些問題可能在程式碼審查時漏掉,引發無窮的麻煩。

捕獲變數怪異之處

下面的程式碼會返回什麼:

for (var i = 0; i < 10; i++) {
    setTimeout(function() { console.log(i); }, 100 * i);
}

介紹一下,setTimeout會在若干毫秒的延時後執行一個函式(等待其它程式碼執行完畢)。

好吧,看一下結果:

10
10
10
10
10
10
10
10
10
10

很多JavaScript程式設計師對這種行為已經很熟悉了。 大多數人期望輸出結果是這樣:

0
1
2
3
4
5
6
7
8
9

還記得我們上面提到的捕獲變數嗎?

我們傳給setTimeout的每一個函式表示式實際上都引用了相同作用域裡的同一個i。

讓我們花點時間思考一下這是為什麼。 setTimeout在若干毫秒後執行一個函式,並且是在for迴圈結束後。 for迴圈結束後,i的值為10。 所以當函式被呼叫的時候,它會列印出 10!

一個通常的解決方法是使用立即執行的函式表示式(IIFE)來捕獲每次迭代時i的值:

for (var i = 0; i < 10; i++) {
    (function (i) {
        setTimeout(function () { console.log(i); }, 100 * i);
    })(i);
}

執行後的結果如下

0
1
2
3
4
5
6
7
8
9

這種奇怪的形式我們已經司空見慣了。 引數 i會覆蓋for迴圈裡的i,但是因為我們起了同樣的名字,所以我們不用怎麼改for迴圈體裡的程式碼。

本例項結束實踐專案地址

https://github.com/durban89/typescript_demo.git
tag: 1.0.4


相關文章