定义

ES6是ECMA为JavaScript制定的第6个标准版本,标准委员会决定,标准在每年6月正式发布并作为当年的正式版本,接下来的时间里就在此版本的基础上进行改动,直到下一年6月草案就自然变成新一年的版本,这样一来就无需以前的版本号,只要用年份标记即可。ECMAscript 2015是在2015年6月发布ES6的第一个版本。以此类推,ECMAscript 2016是ES6的第二个版本、 ECMAscript 2017是ES6的第三个版本。

ES6既是一个历史名词也是一个泛指,含义是5.1版本以后的JavaScript下一代标准,目前涵盖了ES2015、ES2016、ES2017、ES2018、ES2019、ES2020。
所以很多文章提到的es7,es8等,实质上都是不规范的概念,从ES1到ES6,每个标准都是花了好几年甚至十多年才制定下来,你一个ES6到ES7,ES7到ES8,才用了一年,按照这样的定义下去,那不是很快就ES20了。用正确的概念来说ES6目前涵盖了ES2015、ES2016、ES2017、ES2018、ES2019、ES2020。

另外,ES6更新的内容主要分为以下几点

  • 表达式:声明、解构赋值

  • 内置对象:字符串扩展、数值扩展、对象扩展、数组扩展、函数扩展、正则扩展、SymbolSetMapProxyReflect

  • 语句与运算:ClassModuleIterator

  • 异步编程:PromiseGeneratorAsync

更新内容

声明

  • const:声明常量,必须立马赋值
  • let:声明变量,可立马赋值或使用时赋值

注意

不允许重复声明,未定义就使用会报错,出现暂时性死区,不存在变量提升
const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。
但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。
const foo = {};

// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123

// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only

ES2017

共享内存和原子操作:由全局对象SharedArrayBufferAtomics实现,将数据存储在一块共享内存空间中,这些数据可在JS主线程web-worker线程之间共享

ES2020

  • globalThis:作为顶层对象,指向全局环境下的this

  • Browser:顶层对象是window

  • Node:顶层对象是global

  • WebWorker:顶层对象是self

  • 以上三者:通用顶层对象是globalThis

ES提案

  • do表达式:封装块级作用域的操作,返回内部最后执行表达式的值(do{})

  • throw表达式:直接使用throw new Error(),无需(){}包括

  • !#命令:指定脚本执行器(写在文件首行)

解构赋值

  • 字符串解构:const [a, b, c, d, e] = "hello"

  • 数值解构:const { toString: s } = 123

  • 布尔解构:const { toString: b } = true

  • 对象解构

    • 形式:const { x, y } = { x: 1, y: 2 }
    • 默认:const { x, y = 2 } = { x: 1 }
    • 改名:const { x, y: z } = { x: 1, y: 2 }
  • 数组解构

    • 规则:数据结构具有Iterator接口可采用数组形式的解构赋值
    • 形式:const [x, y] = [1, 2]
    • 默认:const [x, y = 2] = [1]
  • 函数参数解构

    • 数组解构:function Func([x = 0, y = 1]) {}
    • 对象解构:function Func({ x = 0, y = 1 } = {}) {}

应用场景

  • 交换变量值:[x, y] = [y, x]
  • 返回函数多个值:const [x, y, z] = Func()
  • 定义函数参数:Func([1, 2])
  • 提取JSON数据:const { name, version } = packageJson
  • 定义函数参数默认值:function Func({ x = 1, y = 2 } = {}) {}
  • 遍历Map结构:for (let [k, v] of Map) {}
  • 输入模块指定属性和方法:const { readFile, writeFile } = require("fs")

注意

  • 匹配模式:只要等号两边的模式相同,左边的变量就会被赋予对应的值
  • 解构赋值规则:如果等号右边的值不是对象或数组,就先将其转为对象
  • 解构默认值生效条件:属性值严格等于undefined
  • 解构遵循匹配模式
  • 解构不成功时变量的值等于undefined
  • undefinednull无法转为对象,因此无法进行解构
let x;
{x} = {x: 1}; 
// SyntaxError: syntax error

这段代码会报错,因为JavaScript引擎会将{x}理解成一个代码块,从而发生语法错误。
只有不将大括号写在行首,避免JavaScript将其解释为代码块,才能解决这个问题。

字符串扩展

2015

  • Unicode表示法:大括号包含表示Unicode字符(\u{0xXX}或\u{0XXX}),有了这种表示法之后,JavaScript 共有 6 种方法可以表示一个字符。
'\z' === 'z'  // true
'\172' === 'z' // true
'\x7A' === 'z' // true
'\u007A' === 'z' // true
'\u{7A}' === 'z' // true
  • 字符串遍历:可通过for-of遍历字符串
for (let codePoint of 'foo') {
  console.log(codePoint)
}
// "f"
// "o"
// "o"

//这个遍历器最大的优点是可以识别大于0xFFFF的码点,传统的for循环无法识别这样的码点。

let text = String.fromCodePoint(0x20BB7);

for (let i = 0; i < text.length; i++) {
  console.log(text[i]);
}
// " "
// " "

for (let i of text) {
  console.log(i);
}
// "𠮷"
  • 字符串模板:可单行可多行可插入变量的增强版字符串
  • 标签模板:函数参数的特殊调用,需要注意的是:模板字符串中变量必须是由非变量包含着的,如果变量在开始位置或者结束位置且没有非变量包含,那么该位置就是空字符串。
var a = 5;
var b = 10;
 
tag`Hello ${ a + b } world ${ a * b }`;
//等同于
tag(['Hello ', ' world ', ''], 15, 50);
  • String.raw():返回把字符串所有变量替换且对斜杠进行转义的结果
let name = "Bob";
String.raw `Hi\n${name}!`;             
// "Hi\nBob!",内插表达式还可以正常运行
  • String.fromCodePoint():返回码点对应字符
String.fromCodePoint(42);       // "*"
String.fromCodePoint(65, 90);   // "AZ"
String.fromCodePoint(0x404);    // "\u0404"
  • codePointAt():// 返回字符对应码点(String.fromCodePoint()的逆操作)

  • normalize():// 把字符的不同表示方法统一为同样形式,
    // 返回新字符串(Unicode正规化)

  • repeat():// 把字符串重复n次,返回新字符串

  • matchAll():// 返回正则表达式在字符串的所有匹配

  • includes():// 是否存在指定字符串

  • startsWith():// 是否存在字符串头部指定字符串

  • endsWith():// 是否存在字符串尾部指定字符串

ES2017

  • padStart():把指定字符串填充到字符串头部,返回新字符串
  • padEnd():把指定字符串填充到字符串尾部,返回新字符串
const str = '123'
str.padStart(str.length + 1, 'a') // => 'a123'

ES2019

  • 直接输入U+2028和U+2029:字符串可直接输入行分隔符和段分隔符
  • JSON.stringify()改造:可返回不符合UTF-8标准的字符串
  • trimStart():消除字符串头部空格,返回新字符串
  • trimEnd():消除字符串尾部空格,返回新字符串

数值扩展

2015

  • 二进制表示法:0b或0B开头表示二进制(0bXX0BXX)
  • 八进制表示法:0o或0O开头表示二进制(0oXX0OXX)
  • Number.EPSILON:数值最小精度
  • Number.MIN_SAFE_INTEGER:最小安全数值(-2^53)
  • Number.MAX_SAFE_INTEGER:最大安全数值(2^53)
  • Number.parseInt():返回转换值的整数部分
  • Number.parseFloat():返回转换值的浮点数部分
  • Number.isFinite():是否为有限数值
  • Number.isNaN():是否为NaN
  • Number.isInteger():是否为整数
  • Number.isSafeInteger():是否在数值安全范围内
  • Math.trunc():返回数值整数部分
  • Math.sign():返回数值类型(正数1、负数-1、零0)
  • Math.cbrt():返回数值立方根
  • Math.clz32():返回数值的32位无符号整数形式
  • Math.imul():返回两个数值相乘
  • Math.fround():返回数值的32位单精度浮点数形式
  • Math.hypot():返回所有数值平方和的平方根
  • Math.expm1():返回e^n - 1
  • Math.log1p():返回1 + n的自然对数(Math.log(1 + n))
  • Math.log10():返回以10为底的n的对数
  • Math.log2():返回以2为底的n的对数
  • Math.sinh():返回n的双曲正弦
  • Math.cosh():返回n的双曲余弦
  • Math.tanh():返回n的双曲正切
  • Math.asinh():返回n的反双曲正弦
  • Math.acosh():返回n的反双曲余弦
  • Math.atanh():返回n的反双曲正切

ES2016

  • 指数运算符(**):数值求幂(相当于Math.pow())

ES2020

  • BigInt:任何位数的整数(新增的数据类型,使用n结尾)
  • BigInt():转换普通数值为BigInt类型
  • BigInt.asUintN():转换BigInt02n-1之间对应的值
  • BigInt.asIntN():转换BigInt-2n-12n-1-1
  • BigInt.parseInt():近似于Number.parseInt(),将一个字符串转换成指定进制的BigInt类型

ES提案

  • 数值分隔符(_):使用_作为千分位分隔符(增加数值的可读性)
  • Math.signbit():返回数值符号是否设置

let budget = 1_000_000_000_000;
budget === 10 ** 12 // true

对象扩展

ES2015

  • 简洁表示法:直接写入变量和函数作为对象的属性和方法({ prop, method() {} })

  • 属性名表达式:字面量定义对象时使用[]定义键([prop],不能与上同时使用)

  • 属性的可枚举性和遍历:描述对象的enumerable

  • super关键字:指向当前对象的原型对象(只能用在对象的简写方法中method() {})

  • Object.is():对比两值是否相等

  • Object.assign():合并对象(浅拷贝),返回原对象

  • Object.getPrototypeOf():返回对象的原型对象

  • Object.setPrototypeOf():设置对象的原型对象

  • proto:返回或设置对象的原型对象

  • 方法的name属性:返回方法函数名

    • 取值函数(getter)和存值函数(setter):get/set函数名(属性的描述对象在get和set上)
    • bind返回的函数:bound函数名
    • Function构造函数返回的函数实例:anonymous

    ES2017

  • Object.getOwnPropertyDescriptors():返回对象所有自身属性(非继承属性)的描述对象

  • Object.values():返回以值组成的数组

  • Object.entries():返回以键和值组成的数组

ES2018

  • 扩展运算符(...):转换对象为用逗号分隔的参数序列({ ...obj },相当于rest/spread参数的逆运算)

ES2019

Object.fromEntries():返回以键和值组成的对象(Object.entries()的逆操作)
ES2020

  • 链判断操作符(?.):是否存在对象属性(不存在返回undefined且不再往下执行)
    • 对象属性:obj?.prop、obj?.[expr]
    • 函数调用:func?.(...args)
  • 空判断操作符(??):是否值为undefinednull,是则使用默认值

数组扩展

ES2015

  • 扩展运算符(...):转换数组为用逗号分隔的参数序列([...arr],相当于rest/spread参数的逆运算)
  • Array.from():转换具有Iterator接口的数据结构为真正数组,返回新数组
  • 类数组对象:包含length的对象、Arguments对象、NodeList对象
  • 可遍历对象:StringSet结构Map结构Generator函数
  • Array.of():转换一组值为真正数组,返回新数组
  • copyWithin():把指定位置的成员复制到其他位置,返回原数组
  • find():返回第一个符合条件的成员
  • findIndex():返回第一个符合条件的成员索引值
  • fill():根据指定值填充整个数组,返回原数组
  • keys():返回以索引值为遍历器的对象
  • values():返回以属性值为遍历器的对象
  • entries():返回以索引值和属性值为遍历器的对象
  • 数组空位:ES6明确将数组空位转为undefined(空位处理规不一,建议避免出现)

ES2016

  • includes():是否存在指定成员

ES2019

  • sort()稳定性:排序关键字相同的项目其排序前后的顺序不变,默认为稳定
  • flat():扁平化数组,返回新数组
  • flatMap():映射且扁平化数组,返回新数组(只能展开一层数组)

函数扩展

ES2015

  • 参数默认值:为函数参数指定默认值
function Func(x = 1, y = 2) {}
  • rest/spread参数(...):返回函数多余参数,以数组的形式存在,之后不能再有其他参数

  • 严格模式:在严格条件下运行JS,如果函数参数使用默认值、解构赋值、扩展运算符,那么函数内部就不能显式设定为严格模式

  • 箭头函数(=>):函数简写

  • 尾调用优化:只保留内层函数的调用帧,不会发生栈溢出,相对节省内存

function f(x) { return g(x); }

ES2017

  • 函数参数尾逗号:允许函数最后一个参数有尾逗号

ES2019

  • toString()改造:返回函数原始代码(与编码一致)
  • catch()参数可省略:catch()中的参数可省略

ES提案

  • 函数部分执行:复用函数功能(?表示单个参数占位符,...表示多个参数占位符)
let obj = {
  f(x, y) { return x + y; },
};

const g = obj.f(?, 3);
g(1) // 4
  • 管道操作符(|>):把左边表达式的值传入右边的函数进行求值(f(x) => x |> f)
  • 绑定运算符(:😃:函数绑定(左边是对象右边是函数,取代bindapplycall调用)
    • bind:bar.bind(foo) => foo::bar
    • apply:bar.apply(foo, arguments) => foo::bar(...arguments)

正则规则

ES2015

  • 变更RegExp构造函数入参:允许首参数为正则对象,尾参数为正则修饰符(返回的正则表达式会忽略原正则表达式的修饰符)
  • 正则方法调用变更:字符串对象的match()replace()search()、split()内部调用转为调用RegExp实例对应的RegExp.prototype[Symbol.方法]
  • u修饰符:Unicode模式修饰符,正确处理大于\uFFFFUnicode字符
  • y修饰符:粘连修饰符,确保匹配必须从剩余的第一个位置开始全局匹配(与g修饰符作用类似)
  • unicode:是否设置u修饰符
  • sticky:是否设置y修饰符
  • flags:返回正则表达式的修饰符

ES2018

  • s修饰符:dotAll模式修饰符,使.匹配任意单个字符(dotAll模式)
  • dotAll:是否设置s修饰符
  • 后行断言:x只有在y后才匹配
  • 后行否定断言:x只有不在y后才匹配
  • Unicode属性转义:匹配符合Unicode某种属性的所有字符
    • 正向匹配:\p{PropRule}
    • 反向匹配:\P{PropRule}
    • 限制:\p{...}\P{...}只对Unicode字符有效,使用时需加上u修饰符
  • 具名组匹配:为每组匹配指定名字(?<GroupName>)

ES2020

  • matchAll():返回所有匹配的遍历器

Symbol

Set

  • 类似于数组的数据结构,成员值都是唯一且没有重复的值, Set描述

Map

类似于对象的数据结构,成员键是任何类型的值, Set描述

proxy

修改某些操作的默认行为,Proxy描述

const proxy = new Proxy(target, handler)

Reflect

保持Object方法的默认行为。reflect

设计目的

  • 将Object属于语言内部的方法放到Reflect上
  • 将某些Object方法报错情况改成返回false
  • 让Object操作变成函数行为
  • Proxy与Reflect相辅相成.

Class

对一类具有共同特征的事物的抽象(实质是基于原型的继承的语法糖), Class描述

Module

  • export:规定模块对外接口
export default Person
  • import:导入模块内部功能
import Person from "person"
  • 复合模式:export命令import命令结合在一起写成一行,变量实质没有被导入当前模块,相当于对外转发接口,导致当前模块无法直接使用其导入变量
export { default } from "person"

其他模块方案

  • CommonJS:用于服务器(动态化依赖)
//CommonJS
// a.js
module.exports = {
    a: 1
}
// or 
exports.a = 1

// b.js
var module = require('./a.js')
module.a // -> log 1
  • AMD:用于浏览器(动态化依赖)
// AMD
define(['./a', './b'], function(a, b) {
  // 加载模块完毕可以使用
  a.do()
  b.do()
})
  • CMD:用于浏览器(动态化依赖)
// CMD
define(function(require, exports, module) {
  // 加载模块
  // 可以把 require 写在函数体的任意地方实现延迟加载
  var a = require('./a')
  a.doSomething()
})
  • UMD:用于浏览器和服务器(动态化依赖)
//UMD
(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        //AMD
        define(['jquery'], factory);
    } else if (typeof exports === 'object') {
        //Node, CommonJS之类的
        module.exports = factory(require('jquery'));
    } else {
        //浏览器全局变量(root 即 window)
        root.returnExports = factory(root.jQuery);
    }
}(this, function ($) {
    //方法
    function myFunc(){};
    //暴露公共方法
    return myFunc;
}));

terator

为各种不同的数据结构提供统一的访问机制, 主要使用for-of进行遍历, Iterator描述

Promise

包含异步结果操作的对象,Promise描述

Generator

封装多个内部状态的异步编程解决方案

function Wrapper(func) {
    return function(...args) {
        const generator = func(...args);
        generator.next();
        return generator;
    }
}
const print = Wrapper(function*() {
    console.log(`First Input: ${yield}`);
    return "done";
});
print().next("hello");

注意

  • 每次调用next(),指针就从函数头部或上次停下的位置开始执行,直到遇到下一个yield命令或return语句为止
  • 函数内部可不用yield命令,但会变成单纯的暂缓执行函数(还是需要next()触发)
  • yield命令是暂停执行的标记,next()是恢复执行的操作
  • yield命令用在另一个表达式中必须放在圆括号里
  • yield命令用作函数参数或放在赋值表达式的右边,可不加圆括号
  • yield命令本身没有返回值,可认为是返回undefined
  • yield命令表达式为惰性求值,等next()执行到此才求值
  • 函数调用后生成遍历器对象,此对象的Symbol.iterator是此对象本身
  • 在函数运行的不同阶段,通过next()从外部向内部注入不同的值,从而调整函数行为
  • 首个next()用来启动遍历器对象,后续才可传递参数
  • 想首次调用next()时就能输入值,可在函数外面再包一层
  • 一旦next()返回对象的done为true,for-of遍历会中止且不包含该返回对象
  • 函数内部部署try-finally且正在执行try,那么return()`会导致立刻进入finally,执行完finally以后整个函数才会结束
  • 函数内部没有部署try-catch,throw()抛错将被外部try-catch捕获
  • throw()抛错要被内部捕获,前提是必须至少执行过一次next()
  • throw()被捕获以后,会附带执行下一条yield命令
  • 函数还未开始执行,这时throw()抛错只可能抛出在函数外部

Realm
提供沙箱功能,允许隔离代码,防止被隔离的代码拿到全局对象

new Realm().global

Async

异步迭代器(for-await-of):循环等待每个Promise对象变为resolved状态才进入下一步,

终于写完这篇文章了。。。。
呜呜呜😭,小佑太不容易麻烦路过的朋友下方留言,
下面附几张图供大家参考...

附图

108757220200423234938279790038040.png

108757220200423235052481984264978.png

108757220200423235123976393350790.png

Q.E.D.


学而不厌 不耻下问