let
语句声明一个「块级作用域」的本地变量,且可选地将其初始化为一个值。
const
语句声明的「常量」是「块级作用域」,与使用let
语句定义的变量相似。常量的值「不能通过重新赋值来改变」,且「不能重新声明」。
1.语法
❝
let 变量名1 [= 变量值1] [, 变量名2 [= 变量值2]] [, ..., 变量名N [= 变量值N]];
❞
变量名1,变量名2,...,变量名N
:变量名。 必须是合法的标识符。变量值1,变量值2,...,变量值N
:变量的初始值。 可以是任意合法的表达式。
❝
const name1 = value1 [, name2 = value2 [, ... [, nameN = valueN]]];
❞
name1,name2,...,nameN
:常量名称。 可以是任意合法的标识符。value1,value2,...,valueN
:常量值。 可以是任意合法的表达式。
2.描述
let
声明的变量、语句或表达式都被限制在「块级作用域」。与var
关键字不同的是,var
声明的变量只能是全局或整个函数块的。let
和var
不同之处在于前者是「在编译时」才被「初始化」。
const
声明创建一个常量,其作用域可以是「全局或本地声明的块」。 与var
变量不同,全局常量不会变为window
对象的属性。需要一个常数的初始化器,即必须在声明时指定它的值(这是有道理的,因为以后不能更改)。
const
声明创建一个值的只读引用
。但这并不意味着它所持有的值是不可变的,只是变量标识符
「不能重新分配」。比如,在引用内容是对象的情况下,这就意味着可以改变对象的内容(如,其参数)。
const
声明创建常量「不能」和它所在作用域内的其他变量或函数「拥有相同的名称」。
与const
一样,let
也不会在全局声明时(在最顶部的范围)创建window
对象的属性。
3.常量的特性
「注意」: 常量在声明时可以使用大小写,「但通常情况下全部用大写字母」。
- 「不能重新赋值」
- 「不能重新声明」
const MYFAVNUM = 7; // 定义常量MYFAVNUM 并赋值7
MYFAVNUM = 20; // 报错(不能重新赋值)
console.log("my favorite number is: " + MYFAVNUM ); // 输出 7
const MYFAVNUM = 20; //报错(不能重新声明)
var MYFAVNUM = 20; // MYFAVNUM 保留给上面的常量,这个操作会失败
let MYFAVNUM = 20; // 也会报错
// 注意:块范围的性质很重要
if (MYFAVNUM === 7) {
let MYFAVNUM = 20;// 没问题,并且创建了一个块作用域变量 MYFAVNUM
console.log('my favorite number is ' + MYFAVNUM ); // MYFAVNUM 现在为 20
var MYFAVNUM = 20; //报错(这被提升到全局上下文并引发错误)
}
console.log("my favorite number is " + MYFAVNUM ); // MYFAVNUM 依旧为 7
- 「常量需要一个初始值」
const FUN; // 报错Uncaught SyntaxError: Missing initializer in const declaration(常量要求一个初始值)
- 「常量可以定义成对象」
const MY_OBJECT = {"key": "value"};
MY_OBJECT = {"OTHER_KEY": "value"}; // 报错(重写对象和上面一样会失败)
- 「对象属性并不在保护的范围内,下面这个声明会成功执行」
MY_OBJECT.key = "otherValue";
- 「也可以用来定义数组」
const MY_ARRAY = [];
MY_ARRAY.push('A'); // ["A"] // 可以向数组填充数据
MY_ARRAY = ['B']; // 但,将一个新数组赋给变量会引发错误!报错。
4.作用域规则
let
声明的变量只在其声明的「块或子块」中可用,这一点,与var
相似。二者之间「最主要的区别」在于var
声明变量的作用域是「整个封闭函数」。举例子如下:
function varTest() {
var a = 1;
{
var a = 2; // 同样的变量!
console.log(a); // 2
}
console.log(a); // 2
}
varTest();
function letTest() {
let a = 1;
{
let a = 2; // 不同样的变量!
console.log(a); // 2
}
console.log(a); // 1
}
letTest();
在代码和方法的最顶端,let
不像var
一样,let
「不会在全局对象里新建一个属性」。即位于函数或代码顶部的var声明
会给全局对象新增属性, 而let
不会。比如:
var a = 'global';
let b = 'global';
console.log(window.a); // "global"
console.log(window.b); // "undefined"
5. 模仿私有成员
在处理构造函数时,可通过let
声明而不是闭包来创建一个或多个私有成员。
var Thing; //定义全局变量Thing
{
let privateScope = new WeakMap();
let counter = 0;
Thing = function() { //全局变量Thing指向函数对象
this.someProperty = 'fun';
privateScope.set(this, {
hidden: ++counter,
});
};
Thing.prototype.showPublic = function() {
return this.someProperty;
};
Thing.prototype.showPrivate = function() {
return privateScope.get(this).hidden;
};
}
console.log(typeof privateScope); // "undefined" ,privateScope不存在全局作用域
var thing = new Thing();
console.log(thing); // Thing {someProperty: "fun"}
thing.showPublic(); // "fun"
thing.showPrivate(); // 1
可用var
创建和闭包具有相同隐私模式的局部变量,但它们需要函数作用域(通常是模块模式中的IIFE),而不仅仅是上面示例中的块作用域。
6.重复声明
- 在同一个「函数作用域」或「块作用域」中重复声明同一个变量会引起
SyntaxError
(语法错误)。
if (a) {
let b;
let b; //报错:Uncaught SyntaxError: Identifier 'b' has already been declared.
}
下面这种var
与 let
合并的声明方式也会报SyntaxError
错误, 因为var
会将变量提升至块顶部, 这就导致「隐式地」重复声明变量。
let a = 1;
{
var a = 2; // 报错 Uncaught SyntaxError: Identifier 'a' has already been declared
}
- 在
switch语句
中只有一个块,可能因此而遇到错误。
let a = 1;
switch(a) {
case 0:
let b;
break;
case 1:
let b; //报错:Uncaught SyntaxError: Identifier 'b' has already been declared
break;
}
但,需要特别指出的是,一个嵌套在case 子句
中的块会「创建一个新的块作用域的词法环境」,就不会产生上诉重复声明的错误。如下:
let a = 1;
switch(a) {
case 0: { //新的块级作用域
let b;
break;
}
case 1: { //新的块级作用域
let b;
break;
}
}
如果你觉得这篇文章对你有帮助,请点赞支持一下哦!