TypeScript(二)

变量声明

letconst是JavaScript里相对较新的变量声明方式,而let在很多方面与var是相似的,但是可以避免在JavaScript里常见的一些问题.const是对let的一个增强,它能阻止对一个变量再次赋值.
因为TypeScript是JavaScript的超集,所以它本身支持letconst.

var声明

一直以来我们都是通过var关键字定义JavaScript变量:

var 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();
g();//return 11
function f() {
    var a = 1;
    a = 2;
    var b = g();//这里调用了函数,传入了a=2
    a = 3;//这里的赋值a=3并没有传入函数中
    return b;
    //定义函数
    function g() {
        return a;
    }
}
f(); // returns 2
作用域规则

var声明有些奇怪的作用域规则

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

变量虽然定义在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);
}//10个10

在这个for循环中,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);
}

参数i会覆盖for循环里的i,但是因为我们起了同样的名字,所以我们不用怎么改for循环里的代码

let声明

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

let hello = "Hello";

主要的区别不在语法上,而是语义.

块作用域

当用let声明一个变量,它使用的是词法作用域块作用域.不同于使用var声明的变量那样可以在包含它们的函数外访问,块作用域变量在包含它们的块或for循环之外是不能访问的.

function f(input: boolean) {
    let a = 100;
    if (input){
        let b = a + 1;
        return b;
    }
    return b;//报错
}

a 的作用域在f函数体内,而b的作用域是if语句块里.
catch语句里声明的变量也具有同样的作用域规则.

try {
    throw "oh no!";
}
catch (e) {
    console.log("Oh well.")
}
console.log(e);//报错,该变量未定义

拥有块级作用域的变量的另一个特点是,它们不能在声明之前读或写.虽然这些变量始终"存在"与它们的作用域里,但在直到声明它的代码之前的区域都属于暂时性死区.它只能说明我们不能在let语句之前访问它们,而TypeScript可以告诉我们这些信息

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

注意:我们仍然可以在一个拥有块作用域变量被声明前获取它.只是我们不能在变量声明前去调用那个函数,如果生成代码为ES2015运行时会抛出一个错误,然而TypeScript是不会报错的.

function foo(){
    return a;//这里可以使用a变量
}
foo();//这里调用在变量声明前应该报错(但是TypeScript不报错)
let a;
重定义及屏蔽

使用var声明时它不在乎你声明多少次:你只会得到一个.

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

在上面的代码里所有x的声明实际上都引用一个相同的'x',并且这是完全有效的代码,这经常会导致一些bug的出现.而现在,let声明就不会那么宽松了.

let x = 10;
let x = 20;//错误,不能在1个作用域里多次声明'x'

并不是要求两个均是块级作用域的声明TypeScript才会给出一个错误的警告

fun f(x){
    let x = 100;//因为其实在函数的传参过程中,x已经被声明,所以会报错;
}
function g(){
    let x = 100;
    var x = 100;//这里的意思就是说同一个块级作用域下不能声明两个变量(只要有一个使用了let)
}

并不是说块级作用域变量不能用函数作用域变量来声明.而是块级作用域变量需要在明显不同的块里声明

function f(condition, x){
    if (condition){
        let x = 100;
        return x;//这里面的x是if块级作用域里声明的
    }
    return x;//这个x是函数传参时声明的其实省略了else
}
f(false, 0);//0
f(true, 0);//100

在一个嵌套作用域里引入一个新名字的行为称作屏蔽.它是一把双刃剑,它可能不小心引入新问题,同时也可能解决一些错误,例如:

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

这个版本的循环能够得到正确的结果,因为内层循环的i可以屏蔽外层循环的i.通常来说我们应该避免使用屏蔽,但是有些场景又需要利用它,看情况而定

块级作用域变量的获取

获取用var声明的变量时,每次进入一个作用域时创建了一个变量的环境,就算作用域内代码已经执行完毕,这个环境与其捕获的变量依然存在.

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

因为我们已经在city的环境里获取到了city,所以就算if语句执行结束后我们仍然可以访问它.
let声明出现在循环体里拥有完全不同于var的行为,不仅是在循环里引入了一个新的变量环境,而且针对每次迭代都会创建一个新的作用域,这就是我们在使用立即执行的函数表达式时做的事

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

这样就能得到我们想要的结果.

const声明

const声明是声明变量的另一种方式.

const numberLivesForCat = 9;

let声明类似,但是const被赋值后不能再改变.也就是说,const拥有与let相同的作用域规则,但是不能对它们重新赋值.

const num= 1;
const kitty = {
    name: 'xiaoji',
    numLives: num;
}
kitty = {
    name: "jiji",
    numLives: num
}//报错,因为const声明的变量的值是不可变的
//但是可以像下面一样进行变量内部的改变
kitty.name = 'xiaogang';//ok
kitty.numLives--;//ok

就是说,除非使用特使的方法去避免,实际上const变量的内部状态是可修改的.
使用最小特权原则,所有变量除了你计划去修改的都应该使用const。 基本原则就是如果一个变量不需要对它写入,那么其它使用这些代码的人也不能够写入它们,并且要思考为什么会需要对这些变量重新赋值。 使用 const也可以让我们更容易的推测数据的流动。

解构

解构数组

最简单的解构:数组的解构赋值

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

上面的代码相当于:

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

解构作用于已声明的变量:

[first, second] = [second, first];//将两者的值交换一下

作用于函数参数:

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

可以在数组里使用...语法创建剩余变量:

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

当然也可以忽略尾随元素:

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
对象解构
let o = {
    a: 'foo',
    b: 12,
    c: 'bar'
};
let {a, b} = o;
console.log(a);//'foo'
console.log(b);//12

就像数组解构,可以使用没有声明的赋值:

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

注意我们需要用括号将它包起来,因为JavaScript通常会将以{起始的语句解析为一个块,上面这行代码经测试直接运行会报错,还是需要在前面加上let a, b;
可以在对象里使用...语法创建剩余变量

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;

这里的冒号不是指定类型的,如果想指定它的类型,仍然需要在其后写上完整的模式

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

默认值可以让你在属性为undefined时使用缺省值:

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

现在即使bundefined,keepWholeObject函数的变量wholeObject的属性a,b都会有值.

函数声明

解构也能用于函数声明:

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

通常更多情况下是指定默认值:

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

展开

展开操作符与解构相反,它允许你将一个数组展开为另一个数组,或将一个对象展开为另一个对象:

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

展开对象

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

所以这点要注意,默认值的覆盖问题
对象展开仅包含对象自身的可枚举属性,意思是说当你展开一个对象实例时,你会丢失其方法:

class C {
  p = 12;
  m() {
  }
}
let c = new C();
let clone = { ...c };
clone.p; // ok
clone.m(); // error!
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351

推荐阅读更多精彩内容