ES6

目录

  • 前言
  • ECMAScript 6简介
  • let 和 const 命令
  • 变量的解构赋值
  • 字符串的扩展
  • 字符串的新增方法
  • 正则的扩展
  • 数值的扩展
  • 函数的扩展
  • 数组的扩展
  • 对象的扩展
  • 对象的新增方法
  • 运算符的扩展
  • Symbol
  • Set 和 Map 数据结构
  • Proxy
  • Reflect
  • Promise 对象
  • Iterator 和 for...of 循环
  • Generator 函数的语法
  • Generator 函数的异步应用
  • async 函数
  • Class 的基本语法
  • Class 的继承
  • Module 的语法
  • Module 的加载实现
  • 编程风格
  • 读懂规格
  • 异步遍历器
  • ArrayBuffer
  • 最新提案
  • Decorator

基础语法

Object

Object.is()
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true

Object.defineProperty(Object, 'is', {
  value: function(x, y) {
    if (x === y) {
      // 针对+0 不等于 -0的情况
      return x !== 0 || 1 / x === 1 / y;
    }
    // 针对NaN的情况
    return x !== x && y !== y;
  },
  configurable: true,
  enumerable: false,
  writable: true
});
Object.assign()

???

Object.getOwnPropertyDescriptors()

运算符的扩展

// 指数运算符,右结合
a ** b
a **= 2

// 可选链
?.

// Null 判断
??

// 逻辑赋值


编程风格

let 取代 var 全局不应该设置变量,而应该用 const 设置常量 所有的函数应该设置为常量 使用严格模式 静态字符串使用单引号或反引号 动态字符串使用双引号 使用解构赋值,包括函数参数、返回值 多行定义的对象最后一个成员以逗号结尾 尽量不修添加对象属性,如果修改,使用Object.assign()

基础

  • 变量提升 & 暂时性死区
  • let 和 const 的全局变量不再属于顶层对象的属性

字符串扩展

codePointAt()	// charCodeAt()
String.fromCodePoint() // fromCharCode()
formCodePoint方法定义在String对象上,codePointAt方法定义在字符串的实例对象上。

可以使用for…of

at() // charAt()
normalize() // Unicode正规化,统一字符的不同表示方法
includes(), startsWith(), endsWith()
repeat()
padStart(), padEnd()
String.raw

// 模板字符串1
let str = 'return ' + '`Hello ${name}`';
let func = new Function('name', str);
func('Jack');
// 模板字符串2
let str = '(name) => `Hello ${name}`';
let func = eval.call(null, str);
func('Jack');

正则的扩展

修饰符u,y, ???

数值的扩展

二进制 0b 八进制 0o Number.isFinite() Number.isNaN() Number.parseInt() Number.parseFloat() Number.isInteger() Number.EPSILON Number.MAX_SAFE_INTEGER Number.MIN_SAFE_INTEGER Number.isSageInteger() Math 新增17个方法 Math.trunc() 去掉小数 Math.sign() 判断正负 Math.cbrt() 立方根 Math.clz32() Math.imul() Math.fround() Math.hypot() Math.expm1()、Math.log1p()、Math.log10()、Math.log2() Math.sinh(x)、Math.cosh(x)、Math.tanh(x)、Math.asinh(x)、Math.acosh(x)、Math.atanh(x) =

数组的扩展

扩展运算符

...[]

复制、合并数组,替代ES5的arr.concat()

任何定义了遍历器(Iterator)接口的对象(参阅 Iterator 一章),都可以用扩展运算符转为真正的数组。

Array.from()

函数的扩展

允许默认值 赋值与解构赋值结合 ???

rest 参数

  • 通过声明 function funcName(...args){} 得到 args 是一个数组。
  • rest 参数必须是最后一个参数。
  • 函数的 length 属性不包括 rest 参数。

对比arguments[]思考:

const sortNumbers = () => Array.prototype.slice.call(arguments).sort()

const sortNumbers = (...numbers) => numbers.sort()

// arguments 不能调用数组的方法

箭头函数

var f = v => v
// 等同于
var f = function(v) {
    return v;
}

var f = () => 5

var f = (a, b) => a + b

var f = id => ({ id: id })  // 返回对象需要加括号

// 只有一行且不需要返回值
var f = () => void doesNotReturn();
  • 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
  • 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
  • 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
  • 不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

箭头函数可以用于回调,不会改变this指向。

除了this,以下三个变量在箭头函数之中也是不存在的,指向外层函数的对应变量:arguments、super、new.target。

Symbol

注意,Symbol函数前不能使用new命令,否则会报错。这是因为生成的 Symbol 是一个原始类型的值,不是对象。也就是说,由于 Symbol 值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型。

Promise

var a = 0;
const p1 = new Promise(function (resolve, reject) {
  setTimeout(() => {
    console.log('p1')
    resolve('0')
  }, 10000)
})
// for(var i=0;i<999999999;i++) {
//     a+=i;
// }

const p2 = new Promise(function (resolve, reject) {
  setTimeout(() => {
    console.log('p2')
    resolve(p1)
  }, 10000)
})
// for(var i=0;i<999999999;i++) {
//     a+=i;
// }

const p3 = new Promise(function (resolve, reject) {
  setTimeout(() => {
    console.log('p3')
    resolve(p2)
  }, 20000)
})

p3.then(e => console.log(e))

Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。

Class

class Point{
  constructor(x,y){
    this.x = x;
    this.y = y;
  }
  toString(){
    return 'test';
  }
}

typeof Point // 'function'
Point === Point.prototype.constructor // true

Object.assign(Point.prototype, {
  toValue(){},
  toString(){}
})

类的方法名可以是表达式

Module

CommonJS

运行时加载,无法在编译时做“静态优化”

ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。

CommonJS 模块是对象。

“编译时加载” = 静态加载 -> 静态分析成为可能

这样的设计,固然有利于编译器提高效率,但也导致无法在运行时加载模块。

模块整体加载所在的那个对象(上例是circle),应该是可以静态分析的,所以不允许运行时改变。下面的写法都是不允许的。

import * as circle from './circle';

// 下面两行都是不允许的
circle.foo = 'hello';
circle.area = function () {};

export default

export

export var foo = 'bar';
export var baz = 'qux';

// 也可以
export { foo, bar };

// 可以使用 as 重命名变量
export {
    foo as a,
    bar as b,
    bar as c
};
// 错误1
export 1;

// 错误2
var m = 1;
export m;

// 正确
export var m = 1;

// 正确
var m = 1;
export { m };

// 正确
var n = 1;
export {n as m};

// 正确
export default 1;

export default

使用export default之后,在import的时候可以指定任意名字。不再需要大括号。相当于:

// modules.js
function foo() {}
export {foo as default};

// app.js
import {default as bar} from 'modules';
// 正确
export var a = 1;

// 正确
var a = 1;
export default a;

// 错误
export default var a = 1;

import

import 的变量是只读的。

import { foo, bar } from 'foobar'

import { foo as a} from 'foobar'

import 是编译阶段执行的

静态执行,不能使用表达式。

import { foo } from 'my_module';
import { bar } from 'my_module';

// 等同于
import { foo, bar } from 'my_module';
import _, { each, forEach } from 'lodash';