定义
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更新的内容主要分为以下几点
-
表达式:声明、解构赋值
-
内置对象:字符串扩展、数值扩展、对象扩展、数组扩展、函数扩展、正则扩展、Symbol、
Set
、Map、Proxy
、Reflect -
语句与运算:
Class
、Module
、Iterator -
异步编程:
Promise
、Generator、Async
更新内容
声明
- const:声明常量,必须立马赋值
- let:声明变量,可立马赋值或使用时赋值
注意
不允许重复声明,未定义就使用会报错,出现暂时性死区,不存在变量提升
const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。
但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定
的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。
const foo
= {};
// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123
// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only
ES2017
共享内存和原子操作:由全局对象SharedArrayBuffer
和Atomics
实现,将数据存储在一块共享内存空间中,这些数据可在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
undefined
和null
无法转为对象,因此无法进行解构
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开头表示二进制(
0bXX
或0BXX
) - 八进制表示法:0o或0O开头表示二进制(
0oXX
或0OXX
) - 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():转换
BigInt
为0
到2n-1
之间对应的值 - BigInt.asIntN():转换
BigInt
为-2n-1
到2n-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)
- 对象属性:
- 空判断操作符(??):是否值为
undefined
或null
,是则使用默认值
数组扩展
ES2015
- 扩展运算符(...):转换数组为用逗号分隔的参数序列([...arr],相当于rest/spread参数的逆运算)
- Array.from():转换具有
Iterator
接口的数据结构为真正数组,返回新数组 - 类数组对象:包含
length
的对象、Arguments
对象、NodeList
对象 - 可遍历对象:
String
、Set结构
、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)
- 绑定运算符(:😃:函数绑定(左边是对象右边是函数,取代
bind
、apply
、call
调用)- bind:
bar.bind(foo) => foo::bar
- apply:
bar.apply(foo, arguments) => foo::bar(...arguments)
- bind:
正则规则
ES2015
- 变更
RegExp
构造函数入参:允许首参数为正则对象,尾参数为正则修饰符(返回的正则表达式会忽略原正则表达式的修饰符) - 正则方法调用变更:字符串对象的
match()
、replace()
、search()
、split()内部调用转为调用RegExp实例对应的RegExp.prototype[Symbol.
方法] - u修饰符:
Unicode
模式修饰符,正确处理大于\uFFFF
的Unicode
字符 - 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
- 独一无二的值,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
命令本身没有返回值,可认为是返回undefinedyield
命令表达式为惰性求值,等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状态才进入下一步,
终于写完这篇文章了。。。。
呜呜呜😭,小佑太不容易麻烦路过的朋友下方留言,
下面附几张图供大家参考...
评论区