TypeScript(二)

變量聲明

letconst是JavaScript里相對(duì)較新的變量聲明方式,而let在很多方面與var是相似的,但是可以避免在JavaScript里常見的一些問題.const是對(duì)let的一個(gè)增強(qiáng),它能阻止對(duì)一個(gè)變量再次賦值.
因?yàn)門ypeScript是JavaScript的超集,所以它本身支持letconst.

var聲明

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

var a = 10;

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

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

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

function f() {
    var a = 10;
    return function g(){
        var b = a + 1;
        return b;
    }
}
var g = f();
g();//return 11
function f() {
    var a = 1;
    a = 2;
    var b = g();//這里調(diào)用了函數(shù),傳入了a=2
    a = 3;//這里的賦值a=3并沒有傳入函數(shù)中
    return b;
    //定義函數(shù)
    function g() {
        return a;
    }
}
f(); // returns 2
作用域規(guī)則

var聲明有些奇怪的作用域規(guī)則

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

變量雖然定義在if語句里面,但是我們卻可以在語句的外面訪問它.這是因?yàn)?code>var聲明可以在包含它的函數(shù),模塊,命名空間或全局作用域內(nèi)部任何位置被訪問,包含它的代碼塊對(duì)此沒有什么影響,有些人稱此為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;
}//錯(cuò)誤代碼

上面的代碼里,里層的for循環(huán)會(huì)覆蓋變量i,因?yàn)樗?code>i都引用相同的函數(shù)作用域內(nèi)的變量.

變量獲取怪異之處
for (var i = 0; i < 10; i++) {
    setTimeout(function() { console.log(i); }, 100 * i);
}//10個(gè)10

在這個(gè)for循環(huán)中,setTimeout在若干秒后執(zhí)行一個(gè)函數(shù),并且是在for循環(huán)結(jié)束后.for循環(huán)結(jié)束后,i的值為10.所以函數(shù)被調(diào)用的時(shí)候,它會(huì)打印出10.
一個(gè)通常的解決方法是使用立即執(zhí)行的函數(shù)表達(dá)式(IIFE)來捕獲每次迭代時(shí)i的值

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

參數(shù)i會(huì)覆蓋for循環(huán)里的i,但是因?yàn)槲覀兤鹆送瑯拥拿?所以我們不用怎么改for循環(huán)里的代碼

let聲明

除了名字不同外,letvar的寫法一致

let hello = "Hello";

主要的區(qū)別不在語法上,而是語義.

塊作用域

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

function f(input: boolean) {
    let a = 100;
    if (input){
        let b = a + 1;
        return b;
    }
    return b;//報(bào)錯(cuò)
}

a 的作用域在f函數(shù)體內(nèi),而b的作用域是if語句塊里.
catch語句里聲明的變量也具有同樣的作用域規(guī)則.

try {
    throw "oh no!";
}
catch (e) {
    console.log("Oh well.")
}
console.log(e);//報(bào)錯(cuò),該變量未定義

擁有塊級(jí)作用域的變量的另一個(gè)特點(diǎn)是,它們不能在聲明之前讀或?qū)?雖然這些變量始終"存在"與它們的作用域里,但在直到聲明它的代碼之前的區(qū)域都屬于暫時(shí)性死區(qū).它只能說明我們不能在let語句之前訪問它們,而TypeScript可以告訴我們這些信息

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

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

function foo(){
    return a;//這里可以使用a變量
}
foo();//這里調(diào)用在變量聲明前應(yīng)該報(bào)錯(cuò)(但是TypeScript不報(bào)錯(cuò))
let a;
重定義及屏蔽

使用var聲明時(shí)它不在乎你聲明多少次:你只會(huì)得到一個(gè).

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

在上面的代碼里所有x的聲明實(shí)際上都引用一個(gè)相同的'x',并且這是完全有效的代碼,這經(jīng)常會(huì)導(dǎo)致一些bug的出現(xiàn).而現(xiàn)在,let聲明就不會(huì)那么寬松了.

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

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

fun f(x){
    let x = 100;//因?yàn)槠鋵?shí)在函數(shù)的傳參過程中,x已經(jīng)被聲明,所以會(huì)報(bào)錯(cuò);
}
function g(){
    let x = 100;
    var x = 100;//這里的意思就是說同一個(gè)塊級(jí)作用域下不能聲明兩個(gè)變量(只要有一個(gè)使用了let)
}

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

function f(condition, x){
    if (condition){
        let x = 100;
        return x;//這里面的x是if塊級(jí)作用域里聲明的
    }
    return x;//這個(gè)x是函數(shù)傳參時(shí)聲明的其實(shí)省略了else
}
f(false, 0);//0
f(true, 0);//100

在一個(gè)嵌套作用域里引入一個(gè)新名字的行為稱作屏蔽.它是一把雙刃劍,它可能不小心引入新問題,同時(shí)也可能解決一些錯(cuò)誤,例如:

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.通常來說我們應(yīng)該避免使用屏蔽,但是有些場(chǎng)景又需要利用它,看情況而定

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

獲取用var聲明的變量時(shí),每次進(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();
} // Seattle

因?yàn)槲覀円呀?jīng)在city的環(huán)境里獲取到了city,所以就算if語句執(zhí)行結(jié)束后我們?nèi)匀豢梢栽L問它.
當(dāng)let聲明出現(xiàn)在循環(huán)體里擁有完全不同于var的行為,不僅是在循環(huán)里引入了一個(gè)新的變量環(huán)境,而且針對(duì)每次迭代都會(huì)創(chuàng)建一個(gè)新的作用域,這就是我們?cè)谑褂昧⒓磮?zhí)行的函數(shù)表達(dá)式時(shí)做的事

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

這樣就能得到我們想要的結(jié)果.

const聲明

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

const numberLivesForCat = 9;

let聲明類似,但是const被賦值后不能再改變.也就是說,const擁有與let相同的作用域規(guī)則,但是不能對(duì)它們重新賦值.

const num= 1;
const kitty = {
    name: 'xiaoji',
    numLives: num;
}
kitty = {
    name: "jiji",
    numLives: num
}//報(bào)錯(cuò),因?yàn)閏onst聲明的變量的值是不可變的
//但是可以像下面一樣進(jìn)行變量?jī)?nèi)部的改變
kitty.name = 'xiaogang';//ok
kitty.numLives--;//ok

就是說,除非使用特使的方法去避免,實(shí)際上const變量的內(nèi)部狀態(tài)是可修改的.
使用最小特權(quán)原則朴爬,所有變量除了你計(jì)劃去修改的都應(yīng)該使用const。 基本原則就是如果一個(gè)變量不需要對(duì)它寫入,那么其它使用這些代碼的人也不能夠?qū)懭胨鼈冋笪⑶乙伎紴槭裁磿?huì)需要對(duì)這些變量重新賦值娘汞。 使用 const也可以讓我們更容易的推測(cè)數(shù)據(jù)的流動(dòng)绰筛。

解構(gòu)

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

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

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

上面的代碼相當(dāng)于:

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

解構(gòu)作用于已聲明的變量:

[first, second] = [second, first];//將兩者的值交換一下

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

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

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

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

當(dāng)然也可以忽略尾隨元素:

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

或其他元素:

let [, second, , fourth] = [1, 2, 3, 4];
console.log(second);//2
consol.log(fourth);//4
對(duì)象解構(gòu)
let o = {
    a: 'foo',
    b: 12,
    c: 'bar'
};
let {a, b} = o;
console.log(a);//'foo'
console.log(b);//12

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

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

注意我們需要用括號(hào)將它包起來,因?yàn)镴avaScript通常會(huì)將以{起始的語句解析為一個(gè)塊,上面這行代碼經(jīng)測(cè)試直接運(yùn)行會(huì)報(bào)錯(cuò),還是需要在前面加上let a, b;
可以在對(duì)象里使用...語法創(chuàng)建剩余變量

let {a, ..passthrough} = o;
let total = passthrough.b + passthrough.c.length;
屬性重命名

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

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

這里可以讀作"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)在即使bundefined,keepWholeObject函數(shù)的變量wholeObject的屬性a,b都會(huì)有值.

函數(shù)聲明

解構(gòu)也能用于函數(shù)聲明:

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

通常更多情況下是指定默認(rèn)值:

function f({a, b} = {a: "", b: 0}): void {
    //...
}
f();//ok default to {a: "", b: 0}
function f({ a, b = 0 } = { a: "" }): void {
    // ...
}
f({ a: "yes" }); // ok, default b = 0
f(); // ok, default to {a: ""}, which then defaults b = 0
f({}); // error, 'a' is required if you supply an argument

展開

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

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

展開對(duì)象

let defaults = {food: "spicy", price: "$$", amibiance: "noisy"};
let search = {...defaults, food: "rich"};//{food: "rich", price: "$$", amibiance: "noisy"} 

如果將defaults放在了search后面,則展開為:

{ food: 'spicy', price: '$$', ambiance: 'no
isy' }

所以這點(diǎn)要注意,默認(rèn)值的覆蓋問題
對(duì)象展開僅包含對(duì)象自身的可枚舉屬性,意思是說當(dāng)你展開一個(gè)對(duì)象實(shí)例時(shí),你會(huì)丟失其方法:

class C {
  p = 12;
  m() {
  }
}
let c = new C();
let clone = { ...c };
clone.p; // ok
clone.m(); // error!
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末奄妨,一起剝皮案震驚了整個(gè)濱河市瓤介,隨后出現(xiàn)的幾起案子爹袁,更是在濱河造成了極大的恐慌远荠,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碉咆,死亡現(xiàn)場(chǎng)離奇詭異搓萧,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)闻察,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門邻梆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來守伸,“玉大人,你說我怎么就攤上這事浦妄∧崮。” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵剂娄,是天一觀的道長(zhǎng)蠢涝。 經(jīng)常有香客問我,道長(zhǎng)宜咒,這世上最難降的妖魔是什么惠赫? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮故黑,結(jié)果婚禮上儿咱,老公的妹妹穿的比我還像新娘。我一直安慰自己场晶,他們只是感情好混埠,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著诗轻,像睡著了一般钳宪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上扳炬,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天吏颖,我揣著相機(jī)與錄音,去河邊找鬼恨樟。 笑死半醉,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的劝术。 我是一名探鬼主播缩多,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼养晋!你這毒婦竟也來了衬吆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤绳泉,失蹤者是張志新(化名)和其女友劉穎逊抡,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體零酪,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡秦忿,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年麦射,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片灯谣。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡潜秋,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出胎许,到底是詐尸還是另有隱情峻呛,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布辜窑,位于F島的核電站钩述,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏穆碎。R本人自食惡果不足惜牙勘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望所禀。 院中可真熱鬧方面,春花似錦、人聲如沸色徘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽褂策。三九已至横腿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間斤寂,已是汗流浹背耿焊。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留遍搞,地道東北人罗侯。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像尾抑,于是被迫代替她去往敵國(guó)和親歇父。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蒂培,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 特別說明再愈,為便于查閱,文章轉(zhuǎn)自https://github.com/getify/You-Dont-Know-JS...
    殺破狼real閱讀 568評(píng)論 0 0
  • 官方中文版原文鏈接 感謝社區(qū)中各位的大力支持护戳,譯者再次奉上一點(diǎn)點(diǎn)福利:阿里云產(chǎn)品券翎冲,享受所有官網(wǎng)優(yōu)惠,并抽取幸運(yùn)大...
    HetfieldJoe閱讀 3,037評(píng)論 3 37
  • 本文屬個(gè)人筆記缴渊,不做詳解赏壹,僅供參考! let命令 基本用法 ES6 新增了let命令衔沼,用來聲明變量蝌借。它的用法類似于...
    R_yan閱讀 29,019評(píng)論 6 18
  • let 命令 塊級(jí)作用域 const 命令 頂層對(duì)象的屬性 global 對(duì)象 let 命令 基本用法 ES6 新...
    嘉奇呦_nice閱讀 1,631評(píng)論 0 2
  • java是一種計(jì)算機(jī)編程語言,擁有跨平臺(tái)指蚁、面向?qū)ο笃杏印⒎盒途幊痰奶匦裕瑥V泛應(yīng)用于企業(yè)級(jí)Web應(yīng)用開發(fā)和移動(dòng)應(yīng)用...
    丶Anger流年閱讀 292評(píng)論 0 4