1.let
(1)let用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效;var是全局变量,let是局部变量;
例如:
分别用let和var声明了两个变量;然后在代码块之外调用这两个变量,结果let声明的变量报错,var声明的变量返回了正确的值;这表明,let声明的变量只在它所在的代码块有效。
{
let a = 10;
var b = 1;
}
console.log(b); // 1
console.log(a); // 报错a没有定义:ReferenceError: a is not defined.
(2)for循环的计数器,就很合适使用let命令;
例如:
使用var,最后输出的是10:
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 10
使用var,最后输出的是6:
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 6
(3)不存在变量提升;
例如:
var可能会变量提升:
console.log(d) //undefined
var d = 2
let不会变量提升 必须先声明后使用:
console.log(e) //报错:Uncaught ReferenceError: e is not defined
let e = 3
(4)暂时性死区;
var命令会发生“变量提升”现象,即变量可以在声明之前使用,值为undefined。为了纠正这种现象,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
命令声明变量之前,该变量都是不可用的;
(5)块级作用域;
let实际上为JavaScript新增了块级作用域,在{ }被包围的范围外,不受内层的let变量影响(但会受var的“变量提升”影响);
例如:
function text1(){
let n = 5; //或var n = 5;
if (true) {
let n = 10;
}
console.log(n); // 5
}
function text2(){
var n = 5;
if (true) {
var n = 10;
}
console.log(n); // 10
}
function text3(){
let n = 5;
if (true) {
var n = 10; //报错,已经声明了n
}
}
(6)不允许重复声明;
let不允许在相同作用域内,重复声明同一个变量,不能在函数内部相同模块范围重新声明参数;
例如:
let a = 10;// 即使声明是var a = 10;后面一样报错
let a = 1;// 报错
function func(arg) {
let arg; // 调用时因同范围重名报错
}
function func(arg) {
{
let arg; // 不报错,因为对上一个arg来看在子模块中
}
}
2.const命令
(1)const声明一个只读的常量;一旦声明,常量的值就不能改变;通常变量名全部大写;
例如:
const PI = 3.1415;
PI // 3.1415
PI = 3;
// TypeError: Assignment to constant variable.
上面代码表明改变常量的值会报错;
(2)一般用于全局变量;且声明时必须立即初始化,不能留到以后赋值;
例如:
const foo;
// SyntaxError: Missing initializer in const declaration
上面代码表示,对于const来说,只声明不赋值,就会报错;
(3)const的作用域与let命令相同:只在声明所在的块级作用域内有效;
例如:
if (true) {
const MAX = 5;
}
MAX // Uncaught ReferenceError: MAX is not defined
(4)const命令声明的常量也是不提升,同样存在暂时性死区,只能在声明的位置后面使用;
例如:
if (true) {
console.log(MAX); // ReferenceError
const MAX = 5;
}
上面代码在常量MAX声明之前就调用,结果报错;
(5)const声明的常量,也与let一样不可重复声明;
例如:
var message = "Hello!";
let age = 25;
// 以下两行都会报错
const message = "Goodbye!";
const age = 30;
3.变量的解构赋值:
ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring);
(1)数组的解构赋值;
a.“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值;
例如:
let [a, b, c] = [1, 2, 3];
b.使用嵌套数组进行解构:
例如:
let a = 1;
let b = 2;
let c = 3;
// 等价于
let [a, b, c] = [1, 2, 3]; // 这种写法属于“模式匹配”
let [ , third] = ["foo", "bar", "baz"];
third // "bar"
let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]
let [x, y, ...z] = ['a'];
x // "a"
y // 变量解构不成功,赋值为undefined
z // 数组解构不成功,赋值为[]
c.不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功;
例如:
let [x, y] = [1, 2, 3];
x // 1
y // 2
let [a, [b], d] = [1, [2, 3], 4];
a // 1
b // 2
d // 4
d.如果等号的右边不是数组(或者严格地说,不是可遍历的结构),将会报错;
例如:
// 报错
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};
e.Set 结构,也可以使用数组的解构赋值;
例如:
let [x, y, z] = new Set(['a', 'b', 'c']);
x // "a"
只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值;
(2)对象的解构赋值;
a.对象也可以解构;
例如:
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
b.对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值;
例如:
let { bar, foo } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
let { baz } = { foo: 'aaa', bar: 'bbb' };
baz // undefined
c.对象的解构赋值,可以很方便地将现有对象的方法,赋值到某个变量;
例如:
// 例一
let { log, sin, cos } = Math;
// 例二
const { log } = console;
log('hello') // hello
d.如果变量名与属性名不一致,必须写成下面这样;
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
f // 'hello'
l // 'world'
e.对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者;
例如:
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
foo // error: foo is not defined
上面代码中,foo是匹配的模式,baz才是变量。真正被赋值的是变量baz,而不是模式foo;
f.与数组一样,解构也可以用于嵌套结构的对象;
例如:
let obj = {
p: [
'Hello',
{ y: 'World' }
]
};
let { p: [x, { y }] } = obj;
x // "Hello"
y // "World"
注意,这时p
是模式,不是变量,因此不会被赋值。如果p
也要作为变量赋值,可以写成下面这样;
let obj = {
p: [
'Hello',
{ y: 'World' }
]
};
let { p, p: [x, { y }] } = obj;
x // "Hello"
y // "World"
p // ["Hello", {y: "World"}]
g.对象的解构也可以指定默认值;
var {x = 3} = {};
x // 3
var {x, y = 5} = {x: 1};
x // 1
y // 5
var {x: y = 3} = {};
y // 3
var {x: y = 3} = {x: 5};
y // 5
var { message: msg = 'Something went wrong' } = {};
msg // "Something went wrong"
(3)字符串的解构赋值;
a.字符串被转换成了一个类似数组的对象;
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
(4)数值和布尔值的解构赋值;
a.解构赋值时,如果等号右边是数值和布尔值,则会先转为对象;
例如:
let {toString: s} = 123;
s === Number.prototype.toString // true
let {toString: s} = true;
s === Boolean.prototype.toString // true
上面代码中,数值和布尔值的包装对象都有toString属性,因此变量s
都能取到值;
(5)函数参数的解构赋值;
a.函数的参数也可以使用解构赋值;
例如:
function add([x, y]){
return x + y;
}
add([1, 2]); // 3
注:本文用于整理知识点和笔记,参考内容来自作者阮一峰《ECMAScript 6 入门》附:https://es6.ruanyifeng.com/#docs/destructuring