ES6一:let-vs-const
ECMAScript 6 – let-vs-const
从今天开始学习ECMAScript 6系列语法
var ??
先看一下 MDN 中对于 var 的定义
FROM MDN
————
- var声明了一个变量,并且可以同时初始化该变量
- 使用var语句声明的变量的作用域是当前执行位置的上下文:一个函数的内部(声明在函数内)或者全局(声明在函数外)。
看个概念—-变量声明提升
FROM MDN
————
由于变量声明(以及其他声明)总是在任意代码执行之前处理的,所以在代码中的任意位置声明变量总是等效于在代码开头声明。这意味着变量可以在声明之前使用,这个行为叫做“hoisting”。“hoisting”就像是把所有的变量声明移动到函数或者全局代码的开头位置。
啥意思?上个例子理解理解
bla = 2
var bla;
// ...
// 可以理解为:
var bla;
bla = 2;
由于这个原因,总是建议在作用域的最开始(函数或者全局代码的开头)声明变量。这样可以使变量的作用域变得清晰。
再看个概念——块级作用域?
块级作用域就不用多说,就是定义的变量只在定义它的块中有效,出了这个块你就不能访问到它了。
在ES6之前,是没有块级作用域这一说的
function getValue(condition) {
if (condition) {
var value = "blue";
// other code
return value;
} else {
// value exists here with a value of undefined
return null;
}
// value exists here with a value of undefined
}
如果此时有块级作用域这个概念的话,会不会觉得在else里面无法访问到value变量, 但是因为变量声明提升,就可以在else里面访问到value变量,只是它未初始化,所以其变量值为undefined。 换句话说,就是因为没有块级作用域的概念,此时这段代码实际解析时像这样:
function getValue(condition) {
var value;
if (condition) {
value = "blue";
// other code
return value;
} else {
return null;
}
}
JavaScript中var声明的作用域像是Photoshop中的油漆桶工具,从声明处开始向前后两个方向扩散,直到触及函数边界才停止扩散。
感觉看到这里,没看出什么重要的,再接着往下看
let ??
FROM MDN
————
- let语句声明一个块级作用域的本地变量,并且可选的赋予初始值
- let允许你声明一个作用域被限制在块级中的变量、语句或者表达式。与var关键字不同的是,var声明的变量只能是全局或者整个函数块的。
例一:块级作用域
function varTest() {
var x = 1;
if (true) {
var x = 2; // 同样的变量!
console.log(x); // 2
}
console.log(x); // 2
}
function letTest() {
let x = 1;
if (true) {
let x = 2; // 不同的变量
console.log(x); // 2
}
console.log(x); // 1
}
例二:for循环
for循环还有一个特别之处,就是循环语句部分是一个父作用域,而循环体内部是一个单独的子作用域。
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
// abc
// abc
// abc
上面代码输出了3次abc,这表明函数内部的变量i和外部的变量i是分离的。
例三:不存在变量提升
var命令会发生”变量提升“现象,即变量可以在声明之前使用,值为undefined
let命令改变了语法行为,它所声明的变量一定要在声明后使用,否则报错。
// var 的情况
console.log(foo); // 输出undefined
var foo = 2;
// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;
例四:暂时性死区
只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。
var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}
上面代码中,存在全局变量tmp,但是块级作用域内let又声明了一个局部变量tmp,导致后者绑定这个块级作用域,所以在let声明变量前,对tmp赋值会报错。
ES6明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。
if (true) {
// TDZ开始
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError
let tmp; // TDZ结束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
上面代码中,在let命令声明变量tmp之前,都属于变量tmp的“死区”。
例五:不允许重复声明
let不允许在相同作用域内,重复声明同一个变量。
// 报错
function () {
let a = 10;
var a = 1;
}
// 报错
function () {
let a = 10;
let a = 1;
}
因此,不能在函数内部重新声明参数。
function func(arg) {
let arg; // 报错
}
function func(arg) {
{
let arg; // 不报错
}
}
例六:typeof
因为“暂时性死区”的问题,也意味着 typeof 不再是一个百分之百安全的操作。
typeof a; ==> ReferenceError
let a;
变量x使用let命令声明,所以在声明之前,都属于x的“死区”,只要用到该变量就会报错。因此,typeof运行时就会抛出一个ReferenceError
typeof b ==>undefined
b 是一个不存在的变量名,结果返回 undefined,所以,在没有let之前,typeof运算符是百分之百安全的,永远不会报错,现在这一点不成立了。这样的设计是为了让大家养成良好的编程习惯,变量一定要在声明之后使用,否则就报错。
const ??
FROM MDN
————
const声明一个只读的常量。一旦声明,常量的值就不能改变
ES6引入的第三个声明类关键词与let类似:const。
const声明的变量与let声明的变量类似,它们的不同之处在于,const声明的变量只可以在声明时赋值,不可随意修改,否则会导致SyntaxError(语法错误)。
下面的例子演示了常量的特性
// 注意: 常量在声明的时候可以使用大小写,但通常情况下会使用全部大写英文。
// 定义常量MY_FAV并赋值7
const MY_FAV = 7;
// 在 Firefox 和 Chrome 这会失败但不会报错(在 Safari这个赋值会成功)
MY_FAV = 20;
// 输出 7
console.log("my favorite number is: " + MY_FAV);
// 尝试重新声明会报错
const MY_FAV = 20;
// MY_FAV 保留给上面的常量,这个操作会失败
var MY_FAV = 20;
// MY_FAV 依旧为7
console.log("my favorite number is " + MY_FAV);
// 下面是一个语法错误
const A = 1; A = 2;
// 常量要求一个初始值,用const声明变量后必须要赋值,否则也抛出语法错误
const FOO; // SyntaxError: missing = in const declaration
// 常量可以定义成对象
const MY_OBJECT = {"key": "value"};
// 重写对象和上面一样会失败
MY_OBJECT = {"OTHER_KEY": "value"};
// 对象属性并不在保护的范围内,下面这个声明会成功执行
MY_OBJECT.key = "otherValue";