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的块级作用域,是通过 letconst 声明变量,来体现的

    • 只要一个代码块 { } 中存在 letconst ,就形成了一个封闭的作用域(块级作用域)

    • 每一个 { } 都是一层块级作用域

  • 块级作用域的出现,实际上使得获得广泛应用的立即执行函数表达式(IIFE)不再必要了

// IIFE 写法
(function () {
    var tmp = ...;
    ...
}());

// 块级作用域写法
{
    let tmp = ...;
    ...
}

四、变量的 声明

1. let 、const 声明的变量 是 局部变量

  • 通过 letconst 声明的变量,只能在当前块级作用域子作用域 被访问到

  • 通过 letconst 声明的变量,window环境中不能访问到

  • ES6 规定

    • varfunction 声明的全局变量,依旧是顶层对象(window)的属性

    • letconstclass 声明的全局变量,不属于顶层对象(window)的属性

var a = 1;
console.log(window.a);  // 1

let b = 1;
console.log(window.b);  // undefined

2. let 、const 声明变量 不存在变量提升

  • letconst 声明变量,不存在变量提升
// var 的情况
console.log(foo); // 输出undefined
var foo = 2;

// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;

3. let 、const 声明变量 不允许重复声明

  • ES6规定:
    • varfunction 声明的变量,重复声明无效,不会报错

    • letconst 、声明的变量,重复声明会报错

// 报错
if (true) {
    const a = 10;
    const a = 1;
}
  • 细节对比:
    • 函数直接作用域内 使用 let 重新声明形参,会报错

    • 内层作用域可以定义外层作用域的同名变量,不会报错

function func(arg) {
    let arg; // 报错
}
function func(arg) {
    {
        let arg; // 不报错
    }
}

4. let 、const 声明变量 存在暂时性死区(TDZ)

  • 暂时性死区(TDZ)
    • 块级作用域中,letconst 声明变量 之前的区域, 就是变量的 暂时性死区

    • 在变量的暂时性死区内,使用变量会报错

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. letconst 区别

  • 区别:

    • 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:
    • undefinednull 无法转为对象,所以对它们进行解构赋值,都会报错
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]
posted @ 2018-01-26 11:40  执着的程序员~  阅读(172)  评论(0编辑  收藏  举报