TypeScript 变量声明

变量声明

letconstJavaScript里相对较新的变量声明方式。 像我们之前提到过的, let在很多方面与var是相似的,但是可以帮助大家避免在JavaScript里的常见一些问题。 const是对let的一个增强,它能阻止对一个变量再次赋值。

1、var 声明

通过var关键字定义JavaScript变量。

var a=10;

这里定义了一个名为a,值为10的变量;也可以在函数内部定义变量:

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

可以在其他函数内部访问相同的变量。

function f(){
    var a = 10;
    return function g(){
        var b = a + 1;
        return b;
    }
}
 
var g = f();
g();  //return 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;
    }
}
 
f(); //return 2;

作用域规则

对于熟悉其它语言的人来说,var声明有些奇怪的作用域规则。

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

变量 x是定义在if语句里面,但是我们却可以在语句的外面访问它。
因为 var声明可以在包含它的函数,模块,命名空间或全局作用域内部任何位置被访问,包含它的代码块对此没有什么影响。
有些人称此为var作用域或 函数作用域。 函数参数也使用函数作用域。

这些作用域规则可能会引发一些错误。 其中之一就是,多次声明同一个变量并不会报错:

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

这里很容易看出一些问题,里层的for循环会覆盖变量i,因为所有i都引用相同的函数作用域内的变量。 有经验的开发者们很清楚,这些问题可能在代码审查时漏掉,引发无穷的麻烦。

let 声明

已经知道了var存在一些问题,这恰好说明了为什么用let语句来声明变量。 除了名字不同外, letvar的写法一致。

let hello = "Hello world!"

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

块作用域

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

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个变量a和b。 a的作用域是f函数体内,而b的作用域是if语句块里。

catch语句里声明的变量也具有同样的作用域规则。

try {
    throw "oh no!";
}
catch (e) {
    console.log("Oh well.");
}
// Error: 'e' doesn't exist here
console.log(e);

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

a ++;  //在声明之前使用'a'是违法的;
let a;

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

function foo() {
    // okay to capture 'a'
    return a;
}
// 不能在'a'被声明前调用'foo'
// 运行时应该抛出错误
foo();
 
let a;

重定义及屏蔽

使用var声明时,它不在乎你声明多少次;你只会得到1个。

function f(x) {
    var x;
    var x;
    if (true) {
        var x;
    }
}
let x = 10;
let x = 20;// 错误,不能在1个作用域里多次声明`x`

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

function f(x) {
    let x = 100; // error: 干扰参数说明
}
 
function g() {
    let x = 100;
    var x = 100; // error: 不能同时声明一个“x”
}

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

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

在一个嵌套作用域里引入一个新名字的行为称做屏蔽。
它是一把双刃剑,它可能会不小心地引入新问题,同时也可能会解决一些错误。
例如,假设我们现在用 let重写之前的sumMatrix函数。

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

const 声明

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

const num = 9;

它们与let声明相似,但是就像它的名字所表达的,它们被赋值后不能再改变。 换句话说,它们拥有与 let相同的作用域规则,但是不能对它们重新赋值。

这很好理解,const引用的值是不可变的。

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

解构

Another TypeScript已经可以解析其它 ECMAScript 2015 特性了。

解构数组 []

最简单的解构莫过于数组的解构赋值了:

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

这创建了2个命名变量 first 和 second。 相当于使用了索引,但更为方便:

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

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

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

作用于函数参数:

ts:
function f([first, second]: [number, number]) {
    console.log(first);
    console.log(second);
}
f([23,2]);
 
 
js:
function f(_a) {
    var first = _a[0], second = _a[1];
    console.log(first);
    console.log(second);
}
f([23, 2]);

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

ts:
let [first, ...rest] = [1, 2, 3, 4];
console.log(first); // outputs 1
console.log(rest); // outputs [ 2, 3, 4 ]
 
 
js:
var _a = [1, 2, 3, 4], first = _a[0], rest = _a.slice(1);
console.log(first);
console.log(rest);

由于是JavaScript, 你可以忽略你不关心的尾随元素:

ts:
let [first] = [1, 2, 3, 4];
console.log(first); // outputs 1
 
 
js:
var first = [1, 2, 3, 4][0];
console.log(first); // outputs 1

或其它元素

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

对象解构 {}

ts:
let o = {
    a: "foo",
    b: 12,
    c: "bar",
}
let { a, b } = o;
 
 
js:
var o = {
    a: "foo",
    b: 12,
    c: "bar",
};
var a = o.a, b = o.b;

属性重命名

可以给属性以不同的名字:

ts:
let { a: newName1, b: newName2 } = o;
 
js:
var newName1 = o.a, newName2 = o.b;

a: newName1 读做 “a 作为 newName1”。 方向是从左到右,写成以下样子:

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

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

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

默认值

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

ts:
function keepWholeObject(wholeObject: { a: string, b?: number }) {
    let { a, b = 1001 } = wholeObject;
}
 
js:
function keepWholeObject(wholeObject) {
    var a = wholeObject.a, _a = wholeObject.b, b = _a === void 0 ? 1001 : _a;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值