TypeScript:let和const变量声明

在开始介绍let和const变量声明前,有必要先了解下JavaScript里的var变量声明。

var变量声明

全局声明

var声明在函数体外,所声明的变量为全局变量。

var name = "张三";

var所声明的全局变量会作为window的一个属性,可以使用"."来引用。如上例的name引用:

console.log(window.name)

注意:非严格模式下,声明在函数体内的变量,把声明语句的var去掉,那么所声明的变量也是全局变量。

作用域

var变量声明的最大特点是它的作用域为声明语句所在的最近函数体内。

示例:

function f() {
    var message = "Hello, world!";

    return message;
}

var所声明的message变量的作用域范围就是在函数f内。

var声明变量的作用域为函数体的全部,隐含着两个主要问题:变量提升和循环内变量共享。

变量提升

变量提升:JavaScript会把函数内的变量声明提升到函数的最顶部。

示例:

function(){
    var a='a';
    var b='b';
    var c='c';
}

等同于

function(){
    var a,b,c;
    a='a';
    b='b';
    c='c';
}

这样看是貌似没有问题,想一下下面的例子输出结果:

var message='message 1';
(function(){
    console.log(message)
    var message ='message 2';
})()

很多人可能会认为控制台输出的结果是message 1。执行一下会发现,输出的结果为undefined。这是因为变量提升了。

上面的例子实际等同于

var message='message 1';
(function(){
    var message;
    console.log(message)
    message ='message 2';
})()

变量提升有它的优势,但也常常给我们带来一些难以发现的bug。

循环内变量共享

直接看示例:

for (var i = 0; i < 10; i++) {
    setTimeout(function() { console.log(i); }, 100 * i);
}

我们期待的输出结果为依次为0,1,2,3,4,5,6,7,8,9,而实际输出结果都为10。这是因为在循环内共享了变量i,i自增到10结束setTimeOut()里的函数还没有调用,当调用函数时,i值为10,所以输出的结果都为10。

为了解决循环内变量共享,可以考虑使用IIFE。

for (var i = 0; i < 10; i++) {
    (function(i) {
        setTimeout(function() { console.log(i); }, 100 * i);
    })(i);
}

重复声明

在上面的例子

var message='message 1';
(function(){
    console.log(message)
    var message ='message 2';
})()

message是允许重复声明的,重复声明的变量会覆盖之前声明的变量。

let变量声明

let是ES6新增的特性,也是为了解决var变量声明所存在的一些问题,可以说let是更完美的var。

基本用法

let varName = 变量值;

示例:

let name = "张三";

这是和var声明变量类似。

注意:如果let变量声明在全局,它并不会像var声明的变量一样成为window的一个属性。

作用域

let变量声明和var最大的不同点就是变量的作用域不一样。var为函数作用域,而let变量声明的为块作用域(block-scoping)。

块作用域会把声明的变量限定在代码块(如使用{}括起来的代码库)或者for循环内,而不是整个函数体。

function f(input: boolean) {
    let a = 100;

    if (input) {
        let b = a + 1;
        return b;
    }
    // 出错: 'b'属于上面的代码块定义的,在代码块外不能使用。
    return b;
}

let声明的变量不允许在声明前使用,这样解决了var变量提升引起的问题。

(function(){
  console.log(message);    //此处会报错,Uncaught ReferenceError: message is not defined
  let message ='my message';
})()

对于循环内的变量,每次循环都会是捕获值的副本作为运算,而不是共享同一个值,解决了var循环内共享变量的问题。所以前面for循环的例子只需把var改为let即可:

for (let i = 0; i < 10; i++) {
    setTimeout(function() { console.log(i); }, 100 * i);
}

重复声明

let是不允许在同一作用域内重复声明,重复声明会报error: can't re-declare 'x' in the same scope。

function f(x) {
    let x = 100; // error: interferes with parameter declaration
}

function g() {
    let x = 100;
    var x = 100; // error: can't have both declarations of 'x'
}

const变量声明

const变量声明和let类似,但如它的名字所寓意,它定义的是常量,包含了两层意思:

  1. 声明的的变量不能被重复赋值
  2. const声明变量是必须立刻赋值
const numLivesForCat = 9;
numLivesForCat = 10;  //重复赋值,错误

const name;  //错误,声明时没有赋值
name = "张三";  

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

参考:
Variable Declarations
深入浅出ES6(十四):let和const
What's the difference between using “let” and “var” to declare a variable?

版权声明:著作权归作者所有。

相关推荐

使用Webpack、TypeScript 和React搭建应用

这里简单介绍基于webpack + typescript + react搭建一个应用。初始化项目创建一个空文件夹demo,使用npm在空文件夹初始化项目。npm init npm install --save-dev webpack npm install --save-dev types

TypeScript:接口(interface)属性的定义

  在TypeScript里,接口扮演了一个定义数据结构的角色,它在TypeScript的类型检查中起到很重要的作用。基本用法匿名定义sayHello(person: {name:string}) { console.log(person.name + ", 您好!"); } 这是一个很简单的例子,它使用匿

Java 9模块声明中requires and requires transitive的区别

可读性(Readability)首先要理解模块的可读性module bar{     requires drink; } bar requires drink意味着:bar模块强制要求存在drink模块,这称为可靠配置。bar模块可以读入drink模块的文件,这称为可读性。bar模块可以访问drink模块的代码,这称为

TypeScript:变量声明之解构赋值

解构(Destructuring)是ES6新增的特性。数组和对象(Object)的一个作用是把一些值组合打包在一起。与之相对,解构就类似把数组里的值或对象的属性解包。使用解构赋值语法可以很简便地把数组的值或对象的属性赋值给单独的变量。语法var a, b, rest; [a, b] = [10, 20]; console.log