第一 : var
1、var声明有些奇怪的作用域规则:
-----var声明可以在包含它的函数,模块,命名空间或全局作用域内部任何位置被访问
function f(shouldInitialize: boolean) {
if (shouldInitialize) {
var x = 10;
}
return x;
}
f(true); // return '10'
f(false); // return 'undefined'
可能引发的错误:多次声明同一个变量并不会报错,可能在代码审查时漏掉,引发无穷的麻烦
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都引用相同的函数作用域内的变量
------这个版本的循环不能得到正确的结果
2、捕获变量的怪异之处
for (var i = 0; i < 10; i++) {
setTimeout(function() { console.log(i); }, 100 * i);
}
输出:
10 10 10 10 10 10 10 10 10 10 10
原因: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);
}
第二:let
let声明使用的是词法作用域或块作用域
1、变量在包含它们的块或for循环之外是不能访问的
function f(input: boolean) {
if (input) {
let b = 1;
return b;
}
// Error: 'b' doesn't exist here
return b;
}
2、变量不能在被声明之前读或写。
---- 虽然这些变量始终“存在”于它们的作用域里,但在直到声明它的代码之前的区域都属于 暂时性死区
function foo() {
return a;
}
// 不能在'a'被声明前调用'foo'
foo();
let a;
3、变量不能重定义
@var定义的变量是可以重定义,但之后得到最后一个
let x = 10;
let x = 20; // 错误,不能在1个作用域里多次声明`x`
4、变量屏蔽
屏蔽:在一个嵌套作用域里引入一个新名字的行为
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。
5、let声明出现在循环体里时拥有完全不同的行为
原理:不仅是在循环里引入了一个新的变量环境,而是针对 每次迭代都会创建这样一个新作用域。** 这就是我们在使用立即执行的函数表达式时做的事**
for (let i = 0; i < 10 ; i++) {
setTimeout(function() {console.log(i); }, 100 * i);
}
输出: 0 1 2 3 4 5 6 7 8 9
第三 : const
1、与let声明相似,但被赋值后不能再改(常量)
2、实际上const变量的内部状态是可修改的。 幸运的是,TypeScript允许你将对象的成员设置成只读的。
注意: let vs const
最小特权原则:所有变量除了你计划去修改的都应该使用const