TypeScript基礎(chǔ)入門 - 變量聲明(二)

轉(zhuǎn)發(fā)地址

TypeScript基礎(chǔ)入門 - 變量聲明(二)

項目實踐倉庫

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

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

npm install -D ts-node

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

npx ts-node src/learn_basic_types.ts
npx ts-node 腳本路徑

變量聲明

let 聲明

現(xiàn)在你已經(jīng)知道了var存在一些問題,這恰好說明了為什么用let語句來聲明變量。 除了名字不同外, let與var的寫法一致。

主要的區(qū)別不在語法上,而是語義,我們接下來會深入研究。

塊作用域

當用let聲明一個變量,它使用的是詞法作用域或塊作用域。 不同于使用 var聲明的變量那樣可以在包含它們的函數(shù)外訪問,塊作用域變量在包含它們的塊或for循環(huán)之外是不能訪問的。

function f(input: boolean) {
    let a = 100;

    if (input) {
        // Still okay to reference 'a'
        let b = a + 1;
        return b;
    }

    // Error: 'b' doesn't exist here
    return b;
}
console.log(f3(true));
console.log(f3(false));

運行后結(jié)果類似如下

101
/Users/durban/nodejs/typescript_demo/dist/variable_declarations.js:58
    return b;
    ^

ReferenceError: b is not defined
    at f3 (/Users/durban/nodejs/typescript_demo/dist/variable_declarations.js:58:5)
    at Object.<anonymous> (/Users/durban/nodejs/typescript_demo/dist/variable_declarations.js:61:13)
    at Module._compile (module.js:652:30)
    at Object.Module._extensions..js (module.js:663:10)
    at Module.load (module.js:565:32)
    at tryModuleLoad (module.js:505:12)
    at Function.Module._load (module.js:497:3)
    at Function.Module.runMain (module.js:693:10)
    at startup (bootstrap_node.js:191:16)
    at bootstrap_node.js:612:3

這里我們定義了2個變量a和b。 a的作用域是f函數(shù)體內(nèi),而b的作用域是if語句塊里。

在catch語句里聲明的變量也具有同樣的作用域規(guī)則。

try {
    throw "oh no!";
}
catch (e) {
    console.log("Oh well.");
}

// Error: 'e' doesn't exist here
console.log(e);

運行后結(jié)果如下

Oh well.
/Users/durban/nodejs/typescript_demo/dist/variable_declarations.js:69
console.log(e);
            ^

ReferenceError: e is not defined
    at Object.<anonymous> (/Users/durban/nodejs/typescript_demo/dist/variable_declarations.js:69:13)
    at Module._compile (module.js:652:30)
    at Object.Module._extensions..js (module.js:663:10)
    at Module.load (module.js:565:32)
    at tryModuleLoad (module.js:505:12)
    at Function.Module._load (module.js:497:3)
    at Function.Module.runMain (module.js:693:10)
    at startup (bootstrap_node.js:191:16)
    at bootstrap_node.js:612:3

擁有塊級作用域的變量的另一個特點是,它們不能在被聲明之前讀或?qū)憽?雖然這些變量始終“存在”于它們的作用域里,但在直到聲明它的代碼之前的區(qū)域都屬于 暫時性死區(qū)。 它只是用來說明我們不能在 let語句之前訪問它們,幸運的是TypeScript可以告訴我們這些信息。

a++; // illegal to use 'a' before it's declared;
let a;

注意一點,我們?nèi)匀豢梢栽谝粋€擁有塊作用域變量被聲明前獲取它。 只是我們不能在變量聲明前去調(diào)用那個函數(shù)。 如果生成代碼目標為ES2015,現(xiàn)代的運行時會拋出一個錯誤;然而,現(xiàn)今TypeScript是不會報錯的。

function foo() {
    // okay to capture 'a'
    return a;
}

// 不能在'a'被聲明前調(diào)用'foo'
// 運行時應(yīng)該拋出錯誤
foo();

let a;

關(guān)于暫時性死區(qū)的更多信息,查看這里Mozilla Developer Network.

重定義及屏蔽

我們提過使用var聲明時,它不在乎你聲明多少次;你只會得到1個。示例如下

function f(x) {
    var x;
    var x;

    if (true) {
        var x;
    }
}

在上面的例子里,所有x的聲明實際上都引用一個相同的x,并且這是完全有效的代碼。 這經(jīng)常會成為bug的來源。 好的是, let聲明就不會這么寬松了。示例如下

let x = 10;
let x = 20; // 錯誤,不能在1個作用域里多次聲明`x`

并不是要求兩個均是塊級作用域的聲明TypeScript才會給出一個錯誤的警告。

function f(x) {
    let x = 100; // error: interferes with parameter declaration
}

function g() {
    let x = 100;
    var x = 100; // error: can't have both declarations of 'x'
}

并不是說塊級作用域變量不能用函數(shù)作用域變量來聲明。 而是塊級作用域變量需要在明顯不同的塊里聲明。

function f(condition, x) {
    if (condition) {
        let x = 100;
        return x;
    }

    return x;
}

console.log("========f4=========\n");
console.log(f4(false, 0)); // returns 0
console.log(f4(true, 0));  // returns 100

運行后輸出如下結(jié)果

========f4=========

0
100

在一個嵌套作用域里引入一個新名字的行為稱做屏蔽。 它是一把雙刃劍,它可能會不小心地引入新問題,同時也可能會解決一些錯誤。 例如,假設(shè)我們現(xiàn)在用 let重寫之前的sumMatrix函數(shù)。

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

    return sum;
}

console.log("========== sumMatrix2 \n");
console.log(sumMatrix2([[1],[2]]))

運行后輸出如下結(jié)果

========== sumMatrix2

3

這個版本的循環(huán)能得到正確的結(jié)果,因為內(nèi)層循環(huán)的i可以屏蔽掉外層循環(huán)的i。

通常來講應(yīng)該避免使用屏蔽,因為我們需要寫出清晰的代碼。 同時也有些場景適合利用它,你需要好好打算一下。

塊級作用域變量的獲取

在我們最初談及獲取用var聲明的變量時,我們簡略地探究了一下在獲取到了變量之后它的行為是怎樣的。 直觀地講,每次進入一個作用域時,它創(chuàng)建了一個變量的 環(huán)境。 就算作用域內(nèi)代碼已經(jīng)執(zhí)行完畢,這個環(huán)境與其捕獲的變量依然存在。

function theCityThatAlwaysSleeps() {
    let getCity;

    if (true) {
        let city = "Seattle";
        getCity = function () {
            return city;
        }
    }

    return getCity();
}
console.log('====== theCityThatAlwaysSleeps ======');
console.log(theCityThatAlwaysSleeps());

運行后得到的結(jié)果如下

====== theCityThatAlwaysSleeps ======
Seattle

因為我們已經(jīng)在city的環(huán)境里獲取到了city,所以就算if語句執(zhí)行結(jié)束后我們?nèi)匀豢梢栽L問它。

回想一下前面setTimeout的例子,我們最后需要使用立即執(zhí)行的函數(shù)表達式來獲取每次for循環(huán)迭代里的狀態(tài)。 實際上,我們做的是為獲取到的變量創(chuàng)建了一個新的變量環(huán)境。 這樣做挺痛苦的,但是幸運的是,你不必在TypeScript里這樣做了。

當let聲明出現(xiàn)在循環(huán)體里時擁有完全不同的行為。 不僅是在循環(huán)里引入了一個新的變量環(huán)境,而是針對 每次迭代都會創(chuàng)建這樣一個新作用域。 這就是我們在使用立即執(zhí)行的函數(shù)表達式時做的事,所以在 setTimeout例子里我們僅使用let聲明就可以了。

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

會輸出與預料一致的結(jié)果:

0
1
2
3
4
5
6
7
8
9

const 聲明

const 聲明是聲明變量的另一種方式。

const numLivesForCat = 9;

它們與let聲明相似,但是就像它的名字所表達的,它們被賦值后不能再改變。 換句話說,它們擁有與 let相同的作用域規(guī)則,但是不能對它們重新賦值。這很好理解,它們引用的值是不可變的。

const numLivesForCat = 9;
const kitty = {
    name: "Aurora",
    numLives: numLivesForCat,
}

// Error
// kitty = {
//     name: "Danielle",
//     numLives: numLivesForCat
// };

// all "okay"
kitty.name = "Rory";
kitty.name = "Kitty";
kitty.name = "Cat";
kitty.numLives--;

除非你使用特殊的方法去避免,實際上const變量的內(nèi)部狀態(tài)是可修改的。 幸運的是,TypeScript允許你將對象的成員設(shè)置成只讀的。

let vs. const

現(xiàn)在我們有兩種作用域相似的聲明方式,我們自然會問到底應(yīng)該使用哪個。 與大多數(shù)泛泛的問題一樣,答案是:依情況而定。

使用最小特權(quán)原則,所有變量除了你計劃去修改的都應(yīng)該使用const。 基本原則就是如果一個變量不需要對它寫入,那么其它使用這些代碼的人也不能夠?qū)懭胨鼈?,并且要思考為什么會需要對這些變量重新賦值。 使用 const也可以讓我們更容易的推測數(shù)據(jù)的流動。

本實例結(jié)束實踐項目地址

https://github.com/durban89/typescript_demo.git
tag: 1.0.5
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 這一篇文章主要是來探討,為什么要用const和let替代var,還有ES6新特性:解構(gòu) js里面很多時候,var關(guān)...
    貓神戰(zhàn)閱讀 3,613評論 0 5
  • 變量聲明 let和const是JavaScript里相對較新的變量聲明方式,而let在很多方面與var是相似的,但...
    madisn閱讀 665評論 0 1
  • 概述 TypeScript本質(zhì)上是向JavaScript語言添加了可選的靜態(tài)類型和基于類的面向?qū)ο缶幊?,同時也支持...
    oWSQo閱讀 8,682評論 1 45
  • 一說起“傷疤”,我是不愿提起的。尤其是在我身邊的娃,總覺得因為我的一不小心,似乎會帶來永久的傷害。所以,面對娃存在...
    沒有寫的舒閱讀 424評論 8 9
  • 1.向w3cSchool學習 細致學習html,css,html5,css3,javascript,jquery....
    Addy_Zhou閱讀 572評論 2 4

友情鏈接更多精彩內(nèi)容