
【TS学习系列】3.变量声明
TypeScript中一共有三种声明变量的方式:var 、let 和 const,let 和 const 是JavaScript里相对较新的变量声明方式。在TypeScript官方文档中,推荐使用 let 替代 var 声明变量的方式、
var声明变量
variable
语句声明了一个变量,可选地将其初始化为一个值。
语法
var 变量名 [: 变量类型] = 变量初始值 ;
变量名:可以定义为任何合法标识符。
变量初始值:该值可以为任何合法表达式。
如表示定义一个变量名为 myName 值为 Jane 的字符串:
var myName : string = "Jane";
我们也可以在函数内部定义变量:
(function myFun(){
var hello : string = "Hello!";
console.log(hello);//Hello!
})();
并且,我们也可以在其他函数内部访问相同的变量:
function myFun1(){
var world : string = "World!";
return function myFun2(){
var helloWorld = "Hello " + world;
console.log(helloWorld);
};
}
var cFun1 = myFun1();
cFun1();//Hello World!
var 的作用域
function showFun(shouldInitialize : boolean){
if(shouldInitialize){
var a = 10;
}
console.log(a);
}
showFun(true);//0
showFun(false);//undefined
此处,变量 a 是定义在 <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];
}
}
console.log(sum);
}
sumMatrix([[1,2],[1,3],[1,5,5]]);//3
注,这里多次声明同一个变量是指变量名、变量类型完全一样的变量,如果变量名相同,变量类型不同会报错:
var i : number = 0;
var i : string= 0;//[ts] Type '0' is not assignable to type 'string'.
//[ts] Subsequent variable declarations must have the same type.
//Variable 'i' must be of type 'number', but here has type 'string'.
捕获变量
快速的猜一下下面的代码会输出什么:
for (var i = 0; i < 10; i++) {
//100 * i 毫秒之后输出i
setTimeout(function() {
console.log(i);
}, 100 * i);
}
期望结果:
0 1 2 3 4 5 6 7 8 9
实际结果:
10 10 10 10 10 10 10 10 10 10
还记得上面提到的捕获变量吗?
我们传给 setTimeout 的每一个函数表达式实际上都引用了相同作用域里的同一个 i 值。
这里,setTimeout 在 i * 100 毫秒之后开始执行一个输出 i 值的函数,当执行到这些函数的时候,for 循环已经结束,此时 i 的值为 10 。所以,当函数被调用的时候,它会打印出 10 。
为了解决这种错误,通常的方法是使用立即执行的函数表达式( IIFE )来捕获每次迭代时 i 的值:
for(var i =0; i < 10; i++){
(function(i){
setTimeout(function(){
console.log(i);
}, i * 100);
})(i);
}
let 声明
语法上除了名字不一样,let 与 var 的写法一致,语法:
let 变量名 [: 变量类型] = 变量初始值 ;
如:
let myName : string = "Jane";
它们两者的区别不在语法上,而是语义。
块作用域
当用 let 声明一个变量,它使用的是词法作用域或块作用域。不同于使用 var 声明变量那样可以在包含它们的函数外部访问,块作用域变量在包含它们的块或 for 循环之外是不能访问的:
function myFun3(input : boolean){
let numA : number = 100;
if(input){
let sum = numA + 1;
return sum;
}
return b;//Error, [ts] Cannot find name 'b'
}
在 catch 语句里声明的变量也具有同样的作用域规则:
try{
throw "no";
}
catch(e){
console.log("ok");
}
console.log(e);//Error, [ts] Cannot find name 'e'.
拥有块级作用域的变量的另一个特点是,它们不能在被声明之前读或写。虽然这些变量始终“存在”于它们的作用域里,但在直到声明它的代码之前的区域都属于暂时性死区。它只是用来说明我们不能在 let 语句之前访问它,幸运的是,TypeScript可以告诉我们这些信息:
aStr += aStr;//Error [ts] Block-scoped variable 'aStr' used before its declaration.
let aStr : string ;
重定义及屏蔽
使用 var 声明时,它不在乎声明过多少次,你只会得到一个。
function f(x) {
var x;
var x;
if (true) {
var x;
}
}
在上面的例子中,所有 x 的声明实际上都引用了同一个 x ,并且运行时不会报错,不过这样使用经常会导致bug。而使用 let 声明的时候就不会有这个问题。
let x = 10;
let x = 20;//Error, 不能在一个作用域里多次声明x
除此之外,在以下情况,TypeScript也会给出错误警告,如:
function funA(x)
{
let x =100;//Error: interferes with parameter declaration
}
function funB()
{
let x =100;
var x =100;// error: can't have both declarations of 'x'
}
并不是说块级作用域变量不能用函数作用域变量来声明,而是块级作用域变量需要在明显不同的快里进行声明。
function f(condition, x) {
if (condition) {
let x = 100;
return x;
}
return x;
}
f(false, 0); // returns 0
f(true, 0); // returns 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();
}
因为我们已经在 city 的环境里获取到了city,所以就算 if 语句执行结束后,我们依然可以用。
回想前面 setTimeOut 的例子,可以直接使用 let 实现相同的效果。如:
for (let i =0;i < 10;i++
{
setTimeout(function(){
console.log(i);//打印0~9
},100 * i);
}
const声明
const 声明是声明变量的另一种方式。
const myName = "BillBill";
使用 const 声明的变量与 let 声明的变量类似,它们拥有相同的作用域规则,但是 const 声明的变量不能对它进行重新赋值,它们引用的值是不可变的。如:
const numLivesForCat = 5;
numLivesForCat = 2;//Error: Cannot assign to 'numLivesForCat' because it is a constant or a read-only property.
const kitty = {
name : "Aurora",
numLives : numLivesForCat
};
kitty = { //Error : Cannot assign to 'kitty' because it is a constant or a read-only property.
name : "Jane",
numLives : numLivesForCat
};
//All okey!
kitty.name ="A";
kitty.name ="B";
kitty.name ="C";
kitty.numLives = 10;
除非使用一些特殊的方法避免,而实际上,const 声明的变量的内部状态是可以改变的。为了防止内部状态被改变,我们可以设置属性为只读,后面会详细说明具体的用法。
let VS. const
在具体使用哪种变量声明方式取决于应用场景。
使用最小特权原则,所有除了你计划修改的变量都应该使用 const 。基本原则就是如果一个变量不需要对它写入,那么其他使用这些代码的人也不能够写入它们,并且要思考为什么要对这些变量重新赋值。使用 const 也可以让我们更容易推测数据的流动。

您可能也喜欢

关于隐藏Android应用的状态栏,并全屏显示WebView
2020年4月17日
powershell:无法加载文件ps1,因为在此系统中禁止执行脚本
2019年10月24日