变量与常量

JavaScript 中,曾以 var 关键字来声明变量,但这种声明方式存在很多问题。从 ECMAScript 6 开始,ECMAScript 中引入了新的 let 关键字(用来声明变量),以及新的 const 关键字(用来声明常量)。在 TypeScript 中,也推荐使用 letconst 关键字来进行声明,不再使用落后的 var 关键字(var 关键字存在许多问题,感兴趣的读者可以自行搜索)。接下来,将分别介绍 letconst 关键字的用法。

let关键字

TypeScript 中使用 let 关键字声明变量,语法如下。

let 变量名称:数据类型;

前面的示例都使用 let 关键字来声明变量,因此这里不再讲述其基本用法。接下来,将介绍 let 关键字的一些使用要点。

块级作用域

使用 let 关键字定义的变量具备块级作用域,在作用域外无法使用该变量。

TypeScript 中,以左花括号 “{” 开头、以右花括号 “}” 结尾形成代码块,将多条语句组合到一个代码块中,将其视作一个整体来组织、管理和运行。

代码块将决定变量的作用域,决定其使用范围究竟是整个 TypeScript 程序、某个类还是某个局部代码块。块级作用域可以简单理解为变量的生效范围在它所定义的代码块中。例如,在以下代码中,变量 bif 代码块中定义,因此它的作用域仅在此代码块中,在代码块之外将无法使用。

if (true) {
    let b: number = 30;
    console.log(b);
}
//编译错误:找不到名称"b"。ts(2304)
console.log(b);

上层作用域内的变量可以在下层作用域内直接使用。如果在下层作用域中定义了同名变量,在引用此变量时,只会引用下层作用域的变量,上层作用域的同名变量在此作用域内将暂时失效,无法使用或修改,示例代码如下。

if (true) {
    //块级作用域1
    let b: number = 30;

    {
        //块级作用域2
        //此作用域声明了同名变量b,因此在此作用域内使用该变量时,只会使用此作用域内的变量b,上层作
        //用域的变量b将会暂时失效
        let b: number = 20;
        console.log(b); //输出20
    }

    console.log(b); //输出30

    {
        //块级作用域3
        //由于此作用域内没有声明同名变量,因此使用了上层作用域的变量b,输出30
        console.log(b);
    }
}

全局作用域

全局作用域是最上层的作用域,在以左花括号 “{” 开头、以右花括号 “}” 结尾形成的代码块之外声明的变量都位于全局作用域中,在所有作用域都可以访问全局作用域的变量,示例代码如下。

//全局作用域变量a
let a: number = 1;

if (true) {
    console.log(a);
}

暂时性死区

在用 let 关键字声明某个变量之前,该变量无法使用,这种机制称为暂时性死区。例如,以下代码将直接引起编译错误。

//编译错误:声明之前已使用的块范围变量"a"。ts(2448)
a = 2;
//编译错误:声明之前已使用的块范围变量"a"。ts(2448)
console.log(a);
let a: number = 1;

以下代码虽然不会引起编译错误,但在运行时会报错。

testLog();
function testLog() {
    console.log(b);
}

let b: number = 2;

输出结果如下。

> Uncaught ReferenceError: Cannot access 'b' before initialization

注意,在下层作用域中声明同名变量,会使得上层作用域的同名变量在整个下层作用域中失效,因此在下层作用域声明该同名变量之前,此变量将由于暂时性死区而无法使用,示例代码如下。

let c: number = 1;

function testNumber() {
    //编译错误:声明之前已使用的块范围变量"c"。ts(2448)
    console.log(c);
    let c: number = 2;
}
testNumber(); // Block-scoped variable 'c' used before its declaration.

禁止重复声明

在同一个作用域内,同名变量只能声明一次,否则将引起编译错误。

//编译错误:无法重新声明块范围变量"a"。ts(2451)
let a: number = 1;
//编译错误:无法重新声明块范围变量"a"。ts(2451)
let a: number = 2;

if (true) {
//不在同一个作用域,因此不会引起错误
    let a: number = 3;
}

const关键字

TypeScript 中使用 const 关键字声明常量,语法如下。

const 常量名称:数据类型 = 初始值;

示例代码如下。

const a: number = 1;
const b: boolean = true;
const c: string = "hello";
const d: bigint = 6n;

const 关键字声明的常量和用 let 关键字声明的变量一样,都具备块级作用域、全局作用域、暂时性死区并且禁止重复声明,但变量和常量有一个关键的区别,即常量只能在声明时赋值,且赋值后不可更改,否则会引起编译错误,示例代码如下。

//编译错误:必须初始化 "const" 声明。ts(1155)
const a: number;
const b: string = "hello";
//编译错误:无法分配到 "b",因为它是常数。ts(2588)
b = "world";

具体该使用 let 关键字还是 const 关键字,应该视当时的情况而定。如果一个变量的值在声明后不允许修改,那么应该使用 const 关键字;如果允许修改但不需要修改,那么也应该使用 const 关键字;在其他情况下,则建议使用 let 关键字。