ES6 介绍 及 变量的声明、解构赋值
一、ES6 简介
-
ECMAScript 6.0(以下简称 ES6)是 ES6 是 5.1 版以后的 JavaScript 的下一代标准,涵盖了 ES2015、ES2016、ES2017
-
语法提案的批准流程:一种新的语法从提案到变成正式标准,需要经历五个阶段,如下(一个提案只要能进入 Stage 2,就差不多肯定会包括在以后的正式标准里面)
- Stage 0 - Strawman(展示阶段)
- Stage 1 - Proposal(征求意见阶段)
- Stage 2 - Draft(草案阶段)
- Stage 3 - Candidate(候选人阶段)
- Stage 4 - Finished(定案阶段)
-
babel转码: 一些JS运行运行环境(runtime),可能不完全支持ES6新语法,需要进行 babel 转码,将ES6语法 转为 ES5语法,从而在现有环境执行 Babel 转码器 参考
二、ES6 新特性汇总(待补充)
-
增加块级作用域
-
数据类型的语法扩展
-
语法更加严谨
-
创建数组:
Array.of(3)
--->[3]
-
判断NaN 是否等于 NaN:
Object.is(NaN, NaN)
--->true
-
+0 与 -0 不相等:
Object.is(+0, -0)
--->false
-
三、ES6 新增 块级作用域
-
ES6的块级作用域,是通过
let
、const
声明变量,来体现的-
只要一个代码块
{ }
中存在let
、const
,就形成了一个封闭的作用域(块级作用域) -
每一个
{ }
都是一层块级作用域
-
-
块级作用域的出现,实际上使得获得广泛应用的立即执行函数表达式(IIFE)不再必要了
// IIFE 写法
(function () {
var tmp = ...;
...
}());
// 块级作用域写法
{
let tmp = ...;
...
}
四、变量的 声明
1. let 、const 声明的变量 是 局部变量
-
通过
let
、const
声明的变量,只能在当前块级作用域 及 子作用域 被访问到 -
通过
let
、const
声明的变量,window环境中不能访问到 -
ES6 规定
-
var
、function
声明的全局变量,依旧是顶层对象(window)的属性 -
let
、const
、class
声明的全局变量,不属于顶层对象(window)的属性
-
var a = 1;
console.log(window.a); // 1
let b = 1;
console.log(window.b); // undefined
2. let 、const 声明变量 不存在变量提升
let
、const
声明变量,不存在变量提升
// var 的情况
console.log(foo); // 输出undefined
var foo = 2;
// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;
3. let 、const 声明变量 不允许重复声明
- ES6规定:
-
var
、function
声明的变量,重复声明无效,不会报错 -
let
、const
、声明的变量,重复声明会报错
-
// 报错
if (true) {
const a = 10;
const a = 1;
}
- 细节对比:
-
函数直接作用域内 使用
let
重新声明形参,会报错 -
内层作用域可以定义外层作用域的同名变量,不会报错
-
function func(arg) {
let arg; // 报错
}
function func(arg) {
{
let arg; // 不报错
}
}
4. let 、const 声明变量 存在暂时性死区(TDZ)
- 暂时性死区(TDZ)
-
块级作用域中,
let
、const
声明变量 之前的区域, 就是变量的 暂时性死区 -
在变量的暂时性死区内,使用变量会报错
-
var tmp = 123;
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的“死区”
- 比较隐蔽的暂时性死区(参数部分)
function bar(x = y, y = 2) {
return [x, y];
}
console.log(bar()); // 报错
5. let
、 const
区别
-
区别:
-
let
声明的变量 允许被修改 -
const
声明的变量 不允许被修改;意味着:const
一旦声明变量,就必须立即初始化,不能留到以后赋值,否则会报错
-
let f1; // 不报错
const f2; // SyntaxError: Missing initializer in const declaration
const
声明的本质: 并不是变量的值不得改动,而是变量指向的那个内存地址不得改动-
简单数据类型:值就保存在变量指向的那个内存地址,所以不能有任何改变
-
复杂数据类型:
const
声明的变量 可以改变指针中的 的数据结构
-
// 所以:const 声明复杂数据类型,其数据结构是可变的
const foo = {};
// 为 foo 添加一个属性,可以成功
foo.prop = 123;
ondole.log(foo.prop); // 123
五、变量的 解构赋值
1. 解构赋值 --- 数组
-
数组 解构赋值的内部机制:
-
遵循 结构 + 位置 匹配模式, 描述如下:
-
数组的每一项都是按次序排列的,完全解构赋值时,相同位置 数据结构相同,才能成功的解构赋值; 否则值为
undefined
-
-
分为:完全解构赋值、不完全解构赋值
// 完全结构赋值
let [foo, [[bar], baz]] = [1, [[2], 3]];
console.log(foo); // 1
console.log(bar); // 2
console.log(baz); // 3
// 不完全结构赋值
let [x, y] = [1, 2, 3];
console.log(x); // 1
console.log(y); // 2
// `...a` 代表定义数组
let arr = [1, 2, 3, 4];
let [a, b, ...c] = arr;
console.log(c); // [3, 4]
2. 解构赋值 --- 对象
- 数组 解构赋值的内部机制:
-
遵循 结构 + 相同属性名 匹配模式, 描述如下:
-
对象中的每一项并不是按顺序排列的,解构赋值时根据相同的结构 找到同名属性;将同名属性的值 赋给 定义对象的属性的值(是属性值的解构赋值),否则值为 undefined
-
// 定义的变量名 与 属性名一致
let { bar, foo } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"
const node = {
loc: {
start: {
line: 1,
column: 5
}
}
};
let { loc, loc: { start }, loc: { start: { line }} } = node;
line // 1
loc // Object {start: Object}
start // Object {line: 1, column: 5}
- 注意: 定义的变量名 与 属性名不一致情况下
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
console.log(baz); // "aaa"
3. 解构赋值 --- 数组、对象 指定默认值
- 默认值生效的条件: 数组 / 对象 的属性值 严格等于
undefined
let [x = 1] = [undefined];
console.log(x); // 1
let [y = 1] = [null];
console.log(y); // null
- 如果默认值是一个表达式,那么这个表达式是惰性求值的(只有在用到的时候,才会求值)
function f() {
console.log('aaa');
}
let [x = f()] = [1]; // 无输出
let [x = f()] = []; // aaa
- 默认值可以引用解构赋值的其他变量,但该变量必须已经声
let [x = 1, y = x] = []; // x=1; y=1
let [x = 1, y = x] = [2]; // x=2; y=2
let [x = 1, y = x] = [1, 2]; // x=1; y=2
let [x = y, y = 1] = []; // ReferenceError
3. 解构赋值 --- 字符串、数值、布尔值、null、undefined
- 字符串:
- 解构赋值时,字符串被转换成了一个类似数组的对象
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
- 数值、布尔值:
- 等号右边是数值和布尔值,则会先转为对象
let {toString: s} = 123;
s === Number.prototype.toString // true
let {toString: s} = true;
s === Boolean.prototype.toString // true
- null、undefined:
undefined
和null
无法转为对象,所以对它们进行解构赋值,都会报错
let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError
4. 解构赋值 --- 函数的参数
- 最基本的三种形式如下:
function fn({
x,
y
}) {
return [x, y];
}
function fn({
x = 0,
y = 1
}) {
return [x, y];
}
function fn({
x,
y
} = {
x = 1,
y = 2
}) {
return [x, y];
}
- 举例如下:
function move({
x = 0,
y = 0
} = {}) {
return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]
function foo({
x = 10,
y = 5
}) {
console.log(x, y);
}
foo({}) // 10 5
foo({x: 1}) // 1 5
foo() // TypeError: Cannot read property 'x' of undefined
function foo({
x = 10,
y = 5
} = {}) {
console.log(x, y);
}
foo({}) // 10 5
foo({x: 1}) // 1 5
foo() // 10 5
5. 解构赋值 --- 用途
- 交换变量
let x = 1;
let y = 2;
[x, y] = [y, x];
- 函数 设置默认参数
function(n = 1) {
return n;
}
- 提取 对象 中的数据
let jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
};
let { id, status, data: number } = jsonData;
console.log(id, status, number);
// 42, "OK", [867, 5309]