在typescript里面推荐大家使用
let
关键字来代替大家所熟悉的JavaScript关键字var
。let
关键字是JavaScript的一个新概念,TypeScript实现了它我们先使用typescirpt的语法将var声明变量的几个问题和使用let声明做一个对比
变量声明提升 (作用域规则)
function f(flag: boolean):any {
if (flag) {
var x:number = 10;
}
return x;
}
console.log(f(true));
console.log(f(false));
大家会发现,我们将变量x定义在if语句块内部,但是却能在外部访问它;这就是var的函数作用域;并且flag为false的时候,也会有打印(undefined),而不是说not defined; 这就是var的变量声明提升
如果换为let, x就只能在if块里面使用,当flag为false时,就会显示x没有定义,通过看编译后的源代码我们也能够发现ts转为js,tsc将变量名直接都改变了,当然就找不到x这个变量的定义了
当用let声明一个变量,它使用的是词法作用域或块作用域;块作用域变量在包含它们的块或for循环之外是不能访问的; 并且没有声明提升
多次重复声明同一个变量不会报错
var a:number = 1;
var a:number = 2;
console.log(a)
javascript里面不会提升我们是否多次声明了同一个变量,遇到这种情况,js会对忽略后续的声明;但是会执行它的重新赋值操作
如果换位let, 在编译阶段就不会通过;会告诉我们 无法重新声明块范围变量“a”
for循环作用域问题
for (var i:number = 0; i < 5; i++) {
setTimeout(function f(): void {
console.log(i);
}, 1000);
}
最后的结果不是我们希望的 01234,而是5个5,这是因为我们传给setTimeout的每一个函数表达式实际上都引用了相同作用域里(var声明的变量只有函数作用域和全局作用域,)的同一个i; 在循环结束后,i的值为5。 所以当函数被调用的时候,它会打印出5个5
我们可以利用IIFE立即自执行函数表达式,创造多个函数作用域出来,为获取到的变量创建了一个新的变量环境
for (var i:number = 0; i < 5; i++) {
(function(i) {
setTimeout(function(): void {
console.log(i);
}, 100 * i);
})(i);
}
当let声明出现在循环体里时拥有完全不同的行为。 不仅是在循环里引入了一个新的变量环境,而是针对 每次迭代都会创建这样一个新作用域。 这就是我们在使用立即执行的函数表达式时做的事
for (let i: number = 0; i < 5; i++) {
setTimeout(function f(): void {
console.log(i);
}, 100 * i);
}
const声明是声明变量的另一种方式
与let声明相似,但是就像它的名字所表达的,它们被赋值后不能再改变。 换句话说,它们拥有与 let相同的作用域规则,但是不能对它们重新赋值
let l1: number = 9;
l1 = 10;
const c2: number = 9;
c2 = 10;
但是对于复杂数据类型有的点特殊
const c2: any = {
name: 'jack'
};
下面直接改是不行的,因为相当于给变量b重新开辟了一个内存空间,地址发生了改变,constant类型的值不能更改
c2 = {
name: 'Lucy'
};
但是下面的代码是可以正常执行的,因为改变的是复杂数据类型的值,它们都存储在堆里面,只是一个引用,它们的值就是地址还是保存在栈里面,没有更改
c2.name = 'Lucy';
选择 let还是const
- 所有变量除了你计划去修改的都应该使用const。使用 const也可以让我们更容易的推测数据的流动
- 基本原则就是如果一个变量不需要对它写入,那么其它使用这些代码的人也不能够写入它们,并且要思考为什么会需要对这些变量重新赋值。