亚洲精品中文免费|亚洲日韩中文字幕制服|久久精品亚洲免费|一本之道久久免费

      
      

            <dl id="hur0q"><div id="hur0q"></div></dl>

                TypeScript 變量聲明

                變量聲明

                變量聲明

                let和const是JavaScript里相對(duì)較新的變量聲明方式。 像我們之前提到過(guò)的, let在很多方面與var是相似的,但是可以幫助大家避免在JavaScript里常見(jiàn)一些問(wèn)題。 const是對(duì)let的一個(gè)增強(qiáng),它能阻止對(duì)一個(gè)變量再次賦值。

                因?yàn)門ypeScript是JavaScript的超集,所以它本身就支持let和const。 下面我們會(huì)詳細(xì)說(shuō)明這些新的聲明方式以及為什么推薦使用它們來(lái)代替 var。

                如果你之前使用JavaScript時(shí)沒(méi)有特別在意,那么這節(jié)內(nèi)容會(huì)喚起你的回憶。 如果你已經(jīng)對(duì) var聲明的怪異之處了如指掌,那么你可以輕松地略過(guò)這節(jié)。

                var聲明

                一直以來(lái)我們都是通過(guò)var關(guān)鍵字定義JavaScript變量。

                var a = 10;

                大家都能理解,這里定義了一個(gè)名為a值為10的變量。

                我們也可以在函數(shù)內(nèi)部定義變量:

                function f() { var message = “Hello, world!”; return message;}

                并且我們也可以在其它函數(shù)內(nèi)部訪問(wèn)相同的變量。

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

                上面的例子里,g可以獲取到f函數(shù)里定義的a變量。 每當(dāng) g被調(diào)用時(shí),它都可以訪問(wèn)到f里的a變量。 即使當(dāng) g在f已經(jīng)執(zhí)行完后才被調(diào)用,它仍然可以訪問(wèn)及修改a。

                function f() { var a = 1; a = 2; var b = g(); a = 3; return b; function g() { return a; }}f(); // returns 2

                作用域規(guī)則

                對(duì)于熟悉其它語(yǔ)言的人來(lái)說(shuō),var聲明有些奇怪的作用域規(guī)則。 看下面的例子:

                function f(shouldInitialize: boolean) { if (shouldInitialize) { var x = 10; } return x;}f(true); // returns ’10’f(false); // returns ‘undefined’

                有些讀者可能要多看幾遍這個(gè)例子。 變量 x是定義在*if語(yǔ)句里面*,但是我們卻可以在語(yǔ)句的外面訪問(wèn)它。 這是因?yàn)?var聲明可以在包含它的函數(shù),模塊,命名空間或全局作用域內(nèi)部任何位置被訪問(wèn)(我們后面會(huì)詳細(xì)介紹),包含它的代碼塊對(duì)此沒(méi)有什么影響。 有些人稱此為* var作用域或函數(shù)作用域*。 函數(shù)參數(shù)也使用函數(shù)作用域。

                這些作用域規(guī)則可能會(huì)引發(fā)一些錯(cuò)誤。 其中之一就是,多次聲明同一個(gè)變量并不會(huì)報(bào)錯(cuò):

                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;}

                這里很容易看出一些問(wèn)題,里層的for循環(huán)會(huì)覆蓋變量i,因?yàn)樗衖都引用相同的函數(shù)作用域內(nèi)的變量。 有經(jīng)驗(yàn)的開(kāi)發(fā)者們很清楚,這些問(wèn)題可能在代碼審查時(shí)漏掉,引發(fā)無(wú)窮的麻煩。

                捕獲變量怪異之處

                快速的猜一下下面的代碼會(huì)返回什么:

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

                介紹一下,setTimeout會(huì)在若干毫秒的延時(shí)后執(zhí)行一個(gè)函數(shù)(等待其它代碼執(zhí)行完畢)。

                好吧,看一下結(jié)果:

                10101010101010101010

                很多JavaScript程序員對(duì)這種行為已經(jīng)很熟悉了,但如果你很不解,你并不是一個(gè)人。 大多數(shù)人期望輸出結(jié)果是這樣:

                0123456789

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

                我們傳給setTimeout的每一個(gè)函數(shù)表達(dá)式實(shí)際上都引用了相同作用域里的同一個(gè)i。

                讓我們花點(diǎn)時(shí)間思考一下這是為什么。 setTimeout在若干毫秒后執(zhí)行一個(gè)函數(shù),并且是在for循環(huán)結(jié)束后。 for循環(huán)結(jié)束后,i的值為10。 所以當(dāng)函數(shù)被調(diào)用的時(shí)候,它會(huì)打印出 10!

                一個(gè)通常的解決方法是使用立即執(zhí)行的函數(shù)表達(dá)式(IIFE)來(lái)捕獲每次迭代時(shí)i的值:

                for (var i = 0; i < 10; i++) { // capture the current state of 'i' // by invoking a function with its current value (function(i) { setTimeout(function() { console.log(i); }, 100 * i); })(i);}

                這種奇怪的形式我們已經(jīng)司空見(jiàn)慣了。 參數(shù) i會(huì)覆蓋for循環(huán)里的i,但是因?yàn)槲覀兤鹆送瑯拥拿?,所以我們不用怎么改for循環(huán)體里的代碼。

                let聲明

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

                let hello = “Hello!”;

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

                塊作用域

                當(dāng)用let聲明一個(gè)變量,它使用的是詞法作用域或塊作用域。 不同于使用 var聲明的變量那樣可以在包含它們的函數(shù)外訪問(wèn),塊作用域變量在包含它們的塊或for循環(huán)之外是不能訪問(wè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;}

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

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

                try { throw “oh no!”;}catch (e) { console.log(“Oh well.”);}// Error: ‘e’ doesn’t exist hereconsole.log(e);

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

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

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

                function foo() { // okay to capture ‘a’ return a;}// 不能在’a’被聲明前調(diào)用’foo’// 運(yùn)行時(shí)應(yīng)該拋出錯(cuò)誤foo();let a;

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

                重定義及屏蔽

                我們提過(guò)使用var聲明時(shí),它不在乎你聲明多少次;你只會(huì)得到1個(gè)。

                function f(x) { var x; var x; if (true) { var x; }}

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

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

                并不是要求兩個(gè)均是塊級(jí)作用域的聲明TypeScript才會(huì)給出一個(gè)錯(cuò)誤的警告。

                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’}

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

                function f(condition, x) { if (condition) { let x = 100; return x; } return x;}f(false, 0); // returns 0f(true, 0); // returns 100

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

                function sumMatrix(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;}

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

                通常來(lái)講應(yīng)該避免使用屏蔽,因?yàn)槲覀冃枰獙懗銮逦拇a。 同時(shí)也有些場(chǎng)景適合利用它,你需要好好打算一下。

                塊級(jí)作用域變量的獲取

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

                function theCityThatAlwaysSleeps() { let getCity; if (true) { let city = “Seattle”; getCity = function() { return city; } } return getCity();}

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

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

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

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

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

                0123456789

                const聲明

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

                const numLivesForCat = 9;

                它們與let聲明相似,但是就像它的名字所表達(dá)的,它們被賦值后不能再改變。 換句話說(shuō),它們擁有與 let相同的作用域規(guī)則,但是不能對(duì)它們重新賦值。

                這很好理解,它們引用的值是不可變的。

                const numLivesForCat = 9;const kitty = { name: “Aurora”, numLives: numLivesForCat,}// Errorkitty = { name: “Danielle”, numLives: numLivesForCat};// all “okay”kitty.name = “Rory”;kitty.name = “Kitty”;kitty.name = “Cat”;kitty.numLives–;

                除非你使用特殊的方法去避免,實(shí)際上const變量的內(nèi)部狀態(tài)是可修改的。 幸運(yùn)的是,TypeScript允許你將對(duì)象的成員設(shè)置成只讀的。 接口一章有詳細(xì)說(shuō)明。

                letvs.const

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

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

                跟據(jù)你的自己判斷,如果合適的話,與團(tuán)隊(duì)成員商議一下。

                這個(gè)手冊(cè)大部分地方都使用了let聲明。

                解構(gòu)

                Another TypeScript已經(jīng)可以解析其它 ECMAScript 2015 特性了。 完整列表請(qǐng)參見(jiàn) the article on the Mozilla Developer Network。 本章,我們將給出一個(gè)簡(jiǎn)短的概述。

                解構(gòu)數(shù)組

                最簡(jiǎn)單的解構(gòu)莫過(guò)于數(shù)組的解構(gòu)賦值了:

                let input = [1, 2];let [first, second] = input;console.log(first); // outputs 1console.log(second); // outputs 2

                這創(chuàng)建了2個(gè)命名變量 first 和 second。 相當(dāng)于使用了索引,但更為方便:

                first = input[0];second = input[1];

                解構(gòu)作用于已聲明的變量會(huì)更好:

                // swap variables[first, second] = [second, first];

                作用于函數(shù)參數(shù):

                function f([first, second]: [number, number]) { console.log(first); console.log(second);}f(input);

                你可以在數(shù)組里使用…語(yǔ)法創(chuàng)建剩余變量:

                let [first, …rest] = [1, 2, 3, 4];console.log(first); // outputs 1console.log(rest); // outputs [ 2, 3, 4 ]

                當(dāng)然,由于是JavaScript, 你可以忽略你不關(guān)心的尾隨元素:

                let [first] = [1, 2, 3, 4];console.log(first); // outputs 1

                或其它元素:

                let [, second, , fourth] = [1, 2, 3, 4];

                對(duì)象解構(gòu)

                你也可以解構(gòu)對(duì)象:

                let o = { a: “foo”, b: 12, c: “bar”};let { a, b } = o;

                這通過(guò) o.a and o.b 創(chuàng)建了 a 和 b 。 注意,如果你不需要 c 你可以忽略它。

                就像數(shù)組解構(gòu),你可以用沒(méi)有聲明的賦值:

                ({ a, b } = { a: “baz”, b: 101 });

                注意,我們需要用括號(hào)將它括起來(lái),因?yàn)镴avascript通常會(huì)將以 { 起始的語(yǔ)句解析為一個(gè)塊。

                你可以在對(duì)象里使用…語(yǔ)法創(chuàng)建剩余變量:

                let { a, …passthrough } = o;let total = passthrough.b + passthrough.c.length;

                屬性重命名

                你也可以給屬性以不同的名字:

                let { a: newName1, b: newName2 } = o;

                這里的語(yǔ)法開(kāi)始變得混亂。 你可以將 a: newName1 讀做 “a 作為 newName1″。 方向是從左到右,好像你寫成了以下樣子:

                let newName1 = o.a;let newName2 = o.b;

                令人困惑的是,這里的冒號(hào)不是指示類型的。 如果你想指定它的類型, 仍然需要在其后寫上完整的模式。

                let {a, b}: {a: string, b: number} = o;

                默認(rèn)值

                默認(rèn)值可以讓你在屬性為 undefined 時(shí)使用缺省值:

                function keepWholeObject(wholeObject: { a: string, b?: number }) { let { a, b = 1001 } = wholeObject;}

                現(xiàn)在,即使 b 為 undefined , keepWholeObject 函數(shù)的變量 wholeObject 的屬性 a 和 b 都會(huì)有值。

                函數(shù)聲明

                解構(gòu)也能用于函數(shù)聲明。 看以下簡(jiǎn)單的情況:

                type C = { a: string, b?: number }function f({ a, b }: C): void { // …}

                但是,通常情況下更多的是指定默認(rèn)值,解構(gòu)默認(rèn)值有些棘手。 首先,你需要在默認(rèn)值之前設(shè)置其格式。

                function f({ a=””, b=0 } = {}): void { // …}f();

                上面的代碼是一個(gè)類型推斷的例子,將在本手冊(cè)后文介紹。

                其次,你需要知道在解構(gòu)屬性上給予一個(gè)默認(rèn)或可選的屬性用來(lái)替換主初始化列表。 要知道 C 的定義有一個(gè) b 可選屬性:

                function f({ a, b = 0 } = { a: “” }): void { // …}f({ a: “yes” }); // ok, default b = 0f(); // ok, default to {a: “”}, which then defaults b = 0f({}); // error, ‘a’ is required if you supply an argument

                要小心使用解構(gòu)。 從前面的例子可以看出,就算是最簡(jiǎn)單的解構(gòu)表達(dá)式也是難以理解的。 尤其當(dāng)存在深層嵌套解構(gòu)的時(shí)候,就算這時(shí)沒(méi)有堆疊在一起的重命名,默認(rèn)值和類型注解,也是令人難以理解的。 解構(gòu)表達(dá)式要盡量保持小而簡(jiǎn)單。 你自己也可以直接使用解構(gòu)將會(huì)生成的賦值表達(dá)式。

                展開(kāi)

                展開(kāi)操作符正與解構(gòu)相反。 它允許你將一個(gè)數(shù)組展開(kāi)為另一個(gè)數(shù)組,或?qū)⒁粋€(gè)對(duì)象展開(kāi)為另一個(gè)對(duì)象。 例如:

                let first = [1, 2];let second = [3, 4];let bothPlus = [0, …first, …second, 5];

                這會(huì)令bothPlus的值為[0, 1, 2, 3, 4, 5]。 展開(kāi)操作創(chuàng)建了 first和second的一份淺拷貝。 它們不會(huì)被展開(kāi)操作所改變。

                你還可以展開(kāi)對(duì)象:

                let defaults = { food: “spicy”, price: “$”, ambiance: “noisy” };let search = { …defaults, food: “rich” };

                search的值為{ food: “rich”, price: “$”, ambiance: “noisy” }。 對(duì)象的展開(kāi)比數(shù)組的展開(kāi)要復(fù)雜的多。 像數(shù)組展開(kāi)一樣,它是從左至右進(jìn)行處理,但結(jié)果仍為對(duì)象。 這就意味著出現(xiàn)在展開(kāi)對(duì)象后面的屬性會(huì)覆蓋前面的屬性。 因此,如果我們修改上面的例子,在結(jié)尾處進(jìn)行展開(kāi)的話:

                let defaults = { food: “spicy”, price: “$”, ambiance: “noisy” };let search = { food: “rich”, …defaults };

                那么,defaults里的food屬性會(huì)重寫food: “rich”,在這里這并不是我們想要的結(jié)果。

                對(duì)象展開(kāi)還有其它一些意想不到的限制。 首先,它僅包含對(duì)象 自身的可枚舉屬性。 大體上是說(shuō)當(dāng)你展開(kāi)一個(gè)對(duì)象實(shí)例時(shí),你會(huì)丟失其方法:

                class C { p = 12; m() { }}let c = new C();let clone = { …c };clone.p; // okclone.m(); // error!

                其次,TypeScript編譯器不允許展開(kāi)泛型函數(shù)上的類型參數(shù)。 這個(gè)特性會(huì)在TypeScript的未來(lái)版本中考慮實(shí)現(xiàn)。

                鄭重聲明:本文內(nèi)容及圖片均整理自互聯(lián)網(wǎng),不代表本站立場(chǎng),版權(quán)歸原作者所有,如有侵權(quán)請(qǐng)聯(lián)系管理員(admin#wlmqw.com)刪除。
                用戶投稿
                上一篇 2022年6月22日 12:08
                下一篇 2022年6月22日 12:08

                相關(guān)推薦

                聯(lián)系我們

                聯(lián)系郵箱:admin#wlmqw.com
                工作時(shí)間:周一至周五,10:30-18:30,節(jié)假日休息