1.let 和 const 命令
let 命令
基本用法
ES 6 新增了 let 命令,用来声明变量,它的用法类似于 var ,但是,所声明的变量只在 let 命令所在的代码块中有效。
{
var a=10;
let b=1;
a // ReferenceError: a is not defined.
b // 1
}
这个里面同时用 var 和 let 声明了变量,现在在代码块外调用,let 报错,var正常,这说明 let 只在代码块中有效。
不存在变量提升
// var 的情况
console.log(foo); // 输出undefined
var foo = 2;
// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;
var 发生了变量提升,从脚本开始运行时,foo已经存在了,但是没有值,所以会输出 undefined。let 不会发生变量提升,就说明在运行前,bar 还不存在,这时候用它,会发生错误。
暂时性死区
var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}
ES6中明确规定,如果区域块中存在 let 和 console ,这些区域块对这些声明的变量从一开始就形成了封闭作用域,凡在声明之前就使用这些,就会报错。所以说明在 let 未声明变量之前,该变量是不可用的。
不允许重复声明
let 不允许在同一作用域内重复声明同一变量
// 报错
function func() {
let a = 10;
var a = 1;
}
// 报错
function func() {
let a = 10;
let a = 1;
}
- .let 为JavaScript 新增了块级作用域(let所声明的变量只在块级作用域中有用),而ES5 中是没有的。
- .ES5 规定函数只能在顶层作用域和函数作用域中声明,不能在块级作用域中声明。而ES6 引入了块级作用域,明确允许在块级作用域中声明函数。但函数声明语句的行为类似于 let ,在块级作用域之外不可引用。而且函数的声明会提升到全局作用域或函数作用域的头部,函数声明还会提升到函数作用域的头部。
// 浏览器的 ES6 环境
function f() { console.log('I am outside!'); }
(function () {
if (false) {
// 重复声明一次函数f
function f() { console.log('I am inside!'); }
}
f();
}());
// Uncaught TypeError: f is not a function
这个会报错,因为实际上执行的是下面的代码。
因此,考虑到环境导致的行为差异太大,所以,避免在块级作用域中声明函数。如果确实有需要,应该写成函数表达式,而不是函数声明语句。
// 函数声明语句
{
let a = 'secret';
function f() {
return a;
}
}
// 函数表达式
{
let a = 'secret';
let f = function () {
return a;
};
}
注意:ES6 允许块级作用域中声明函数的规则为:只在使用大括号的情况下成立,没有大括号,就会报错。
const 命令
1.const 声明一个只读的常量,const 的值,一旦声明不能改变。
2.const 声明的变量不得改变值,const 一旦声明变量必须初始化,不能留到后面再赋值,否则会报错。
3.const 所声明的变量与let 相同,只在块级作用域内有效
4.const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用。
变量的解构赋值
解构:从对象或者数组中提取值,对变量进行赋值。
1.数组的解构赋值
基本用法
- 1.解构不成功。如果解构不成功,变量的值就等于undefined。
let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3
let [ , , third] = ["foo", "bar", "baz"];
third // "baz"
let [x, , y] = [1, 2, 3];
x // 1
y // 3
let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]
let [x, y, ...z] = ['a'];
x // "a"
y // undefined
z // []
这种写法要符合“模式匹配”:只要等号两边的模式相同,左边的变量就会被赋予相应的值。
- 2.不完全解构。 等号左边的模式,只匹配等号右边的一部分数组,这种情况下解构可以成功。
let [x, y] = [1, 2, 3];
x // 1
y // 2
let [a, [b], d] = [1, [2, 3], 4];
a // 1
b // 2
d // 4
- 3.如果等号右边的不是数组就会报错
// 报错
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};
默认值
解构赋值允许指定默认值,只有当一个数组成员严格等于undefined的时候,默认值才会有效
let [x = 1] = [undefined];
x // 1
let [x = 1] = [null];
x // null
上面代码中,如果一个数组成员是null,默认值就不会生效,因为null不严格等于undefined。
2.对象的解构赋值
对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
let { bar, foo } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"
let { baz } = { foo: "aaa", bar: "bbb" };
baz // undefined
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
如果变量与属性名不一样,必须写成下面这样:
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
console.log(baz); //aaa
实际上,对象的解构赋值是下面形式的简写:
let { foo: foo, bar: bar } = { foo: "aaa", bar: "bbb" };
也就是说对象的解构赋值,是先找到它的同名属性,然后把值赋给它的变量,真正赋值的是后者而不是前者
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
console.log(baz); //aaa
console.log(foo); //foo is not defined
对象的嵌套解构
let obj = {
p: [
'Hello',
{ y: 'World' }
]
};
let { p, p: [x, { y }] } = obj;
console.log(x) //"hello"
console.log(y) //"world"
console.log(p) //"hello",{y:"world"}
对象的解构也可以指定默认值
var { x: y = 3 } = {};
console.log(x); // x is not defined
console.log(y); //3
var { x: y = 3 } = { x: 5 };
console.log(y); //5
默认值生成的条件是对象的属性值严格等于undefined
var {x = 3} = {x: undefined};
x // 3
var {x = 3} = {x: null};
x // null
如果要将一个已经声明的变量用于解构赋值,必须非常小心
// 错误的写法
let x;
{x} = {x: 1};
// SyntaxError: syntax error
// 正确的写法
let x;
({x} = {x: 1});