注:该文章为持续更新状态,现已更新至 ES5~ES2022
ES简介
全称 ECMAScript
:ECMA国际
通过 ECMA
标准程序设计语言标准,JavaScript 是 ES 标准的实现
ECMA
:European Computer Manufacturers Association(欧洲计算机制造商协会)
- 评估、开发、认可电信和计算机标准,1994年改名为 ECMA 国际
- TC39(Technical Committee 39)委员会推进维护,会员主要有(浏览器厂商,Apple,Google,Microsoft,Intel 等)
ES5(2009)
特性概览
- 严格模式
- JSON
- 扩展对象
- 数组
- 原型
- 字符串
- 日期方法
说明:以上算是 JavaScript 的基本语法和使用了,这里不再赘述
ES6(2015)
let
& const
let
声明变量
- 不能重复
- 块级作用域
- 不存在变量提升
const
声明常量
- 一定要赋初始值
- 无法修改
- 块级作用域
- 可以修改对象和数组里面元素
解构赋值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
/**
* 数组解构
*/
const animal = ['cat', 'dog', 'pig']
const [a,b,c,d] = animal // a='cat',b='dog',c='pig',d=undefined
/**
* 对象解构(ES9 支持)
*/
const person = {
name: 'Lisa',
age: 20,
gender: 'female',
parents: {
father: 'Bob',
mother: 'Jure'
},
}
let { name, age, gender: sex, parents: { father}} = person
// name='Lisa',age=20,gender 重命名为 sex,sex='female',
// 获取parents对象中的father father='Bob'
|
模板字符串 ``
1
2
|
const str = 'hello'
const newStr = `2021,${str},Lisa` // newStr = '2021,hello,Lisa'
|
对象简化写法
1
2
3
4
5
6
7
8
9
10
|
const str = 'hello'
const arr = ['a','b']
const obj = { name: 'Lisa' }
const func() { return null }
const myObject = {
str, //str: 'hello'
arr, // arr: ['a','b']
obj, // obj: { name: 'Lisa' }
func,// func: function() { return null }
}
|
箭头函数
1
2
3
4
|
const sum = (a,b) => {
return a+b
}
sum(1,3) // 4
|
- this:没有自己的 this,this指向外层作用域的最近的 this
- 简写:
- 有且仅有一个参数,可省略 ()
- 函数体仅有一条语句,且是 return 语句,可省略 return 和 {}
高阶函数 & 链式编程(ES5 开始支持)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
// 需求:求数组 arr 中,所有小于 10 的正整数元素,乘以 2 的和
const arr = [ 1, -3, 5 , 10, 6, 9, 32]
const result1 = arr.filter(item => item > 0 && item < 10) // [1,5,6,9]
const result2 = result1.map(item => item * 2 ) //[2,10,12,18]
const result3 = result2.reduce((preReturn, currentItem) => {
const result = preReturn + currentItem
return result
}, initialValue = 0) // 42
// 注意:initialValue 参数可选,忽略时reduce第一次遍历只执行 return 语句
// 链式编程
const result = arr.filter(item => item > 0 && item < 10)
.map(item => item * 2 )
.reduce((preReturn, currentItem) => {
const result = preReturn + currentItem
return result
}, initialValue = 0) // result = 42
|
Symbol 类型
ES6 新增的一个类型,表示一个独一无二的类型,无法和其他类型做运算,常用于给对象添加一个唯一的属性,防止重复
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// 简单创建
const s1 = Symbol('s1') // 参数为描述字符串,可省略
const s2 = Symbol('s1') // s1 != s2
// Symbol.for() 创建
const s3 = Symbol.for('s3')
const s4 = Symbol.for('s3') // s3 = s4
// 用法:给对象添加唯一属性
const person = {
name: 'Lisa',
age: 20
}
const age = Symbol('age')
person[age] = 21
// person = { name: 'Lisa', age: 20, [Symbol('age')]: 21}
|
yield 与 函数生成器异步编程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
/* 获取 用户, 订单, 商品 数据 */
let iterator;
// 异步获取用户
const getUser = () => {
const timer = setTimeout(() => {
const data = '用户数据'
iterator.next(data)
clearTimeout(timer)
}, 1000)
}
// 异步获取订单
const getOrder = () => {
const timer = setTimeout(() => {
const data = '订单数据'
iterator.next(data)
clearTimeout(timer)
}, 1000)
}
// 异步获取商品
const getGood = () => {
const timer = setTimeout(() => {
const data = '商品数据'
iterator.next(data)
clearTimeout(timer)
}, 1000)
}
// 定义函数生成器
function * gen() {
const user = yield getUser() // 执行后挂起,迭代器 next() 后执行后面
const order = yield getOrder() // 执行后挂起,迭代器 next() 后执行后面
const good = yield getGood() // 执行后挂起,迭代器 next() 后执行后面
}
// 第一次调用
iterator = gen()
iterator.next()
|
Promise 异步编程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// 实例化 Promise 对象
const response = new Promise( (resolve, reject) => {
// 模拟请求
const timer = setTimeout(() => {
const data = '获取数据'
const err = '发生错误'
if(data) {
resolve(data)
} else {
reject(err)
}
}, 1000)
})
response.then( data => {console.log('成功')}, err => {console.log('失败')})
.catch( e => { console.log(e)})
|
Set & Map
1
2
3
4
5
6
7
|
const arr = [1,2,3,4,5,5]
const set = new Set(arr) // set = [1,2,3,4,5]
const newArr = [...new Set(arr)] // 数组去重
// 常用方法
set.hasItem(1) // 判断是否含有某个值
set.add(6) // 添加
set.delete(1) // 删除
|
Map
:键值对集合,key 的类型可以是字符串或者 object
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
const map = new Map()
map.set('name', 'Lisa') // 添加
const parents = {
father: 'Bob',
mother: 'Jure'
}
map.set(parents, true) // 添加
map.delete(parents) // 删除
// 遍历
for(let item of map) {
// item 是个数组,第一个元素是key,第二个元素是value
console.log(item[0])
console.log(item[1])
}
|
模块化
1
2
3
4
5
6
7
8
9
|
export arr = [] // 单独暴露
export obj = {} // 单独暴露
export default { // 默认暴露
name: 'cat'
}
const a= 'hello'
const b = []
export { a, b} // 统一暴露
|
1
2
3
4
5
6
7
8
9
|
/**
* defaultObj 默认导入,可任意命名
* arr,a,b 普通导入
* obj as newObj 别名导入
*/
import defaultObj, {arr, obj as newObj, a, b} from '文件路径或模块';
/* 全部导入 */
import * as all from '文件路径或模块';
|
ES2016(开始以年份命名)
幂运算
1
2
3
|
console.log(2**4) // 16
let x = 5
x **= 2 // 25
|
Array.prototype.includes
1
2
3
|
const fruits = ["Banana", "Orange", "Apple", "Mango"];
fruits.includes("Mango"); // 为 true
|
ES2017
async & await
Promise 的语法糖
async
:定义函数的时候,被声明的函数返回值会包装成一个 Promise 对象
await
:调用函数的时候声明,将函数挂起,返回成功的 resolve。被声明函数是一个 Promise 的对象才有效
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
/*
* 返回值不是一个 Promise 对象:
* - 包装成 resolve 的对象
* - 抛出了异常,包装成 reject 的对象
* 返回值是 Promise
*/
const getName = async () => {
return 'Lisa'
}
const response = new Promise((resolve, reject) => {
resolve('成功!')
// reject('失败!')
})
const main = async () => {
try {
const result = await p
console.log(result) // 成功!
} catch(e) {
console.log(e) // 失败!
}
}
|
Object 方法扩展
1
2
3
4
5
6
7
8
|
const obj = {
name: 'Lisa',
age: 18,
gender: 'female'
}
const keys = Object.keys(obj) // ['name', 'age', 'gender']
const entries = Object.entries(obj) // 对象 =》 二维数组
// [['anem', 'Lisa'],['age', 18],['gender', 'female']]
|
ES2018
对象解构
见
解构赋值
,这种写法同时支持对象类型了
正则扩展
命名捕获
1
2
3
4
5
6
7
8
9
10
11
12
|
const str = '<a href="http://www.baidu.com">百度</a>'
const reg = /<a href="(.*)">(.*)<\/a>/
const result = reg.exec(str)
console.log(result[0]) // <a href="http://www.baidu.com">百度</a>
console.log(result[1]) // http://www.baidu.com
console.log(result[2]) // 百度
console.log(result.groups) // undefined
// 命名捕获
const reg2 = /<a href="(?<url>.*)">(?<text>.*)<\/a>/
const result2 = reg2.exec(str)
console.log(result.groups) // {url: 'http://www.baidu.com', text: '百度'}
|
反向断言
1
2
3
4
5
6
7
8
|
const str = 'ES56321你知道么555啦啦啦'
/* 正向断言 */
const reg = /\d+(?=啦)/
const result = reg.exec(str) // result[0] = 555
/* 反向断言 */
const reg2 = /(?<=么)\d+/
const result2 = reg2.exec(str) // result[0] = 555
|
贪婪匹配
1
2
|
// 默认 . 不能匹配换行
const reg = /.*?/gs // 能匹配换行
|
ES2019
Object.fromEntries
将Map或二维数组 => 对象
1
2
3
4
|
const map = new Map()
map.set('name', 'Lisa')
map.set('age', 18)
const obj = Object.fromEntries(map) // { name: 'Lisa', age: 18}
|
字符串扩展-trimStart & trimEnd
清除字符串左侧或者右侧空格
1
2
|
' aaaa'.trimStart() // aaaa
'aaaa '.trimEnd() // aaaa
|
数组扩展-数组降维
1
2
3
4
5
6
|
// Array.flat(int), int 默认为1
const arr = [11,12,13,[24,25,[36,37]]]
const result1 = arr.flat() // [11,12,13,24,25,36,37]
const result2 = arr.flat(2) // [11,12,13,24,25,[36,37]]
// Array.flatMap(),等同于map,如果 return [],降维
|
Symbol 扩展-获取描述
1
2
|
const s = Symbol('hello')
console.log(s.descriptiom) // hello
|
ES2020
Promise 扩展-Promise.allSettled()
1
2
3
4
5
6
7
8
9
10
11
12
|
const p1 = new Promise((resolve, reject) => {
resolve('成功')
})
const p2 = new Promise((resolve, reject) => {
reject('失败')
})
// 返回结果为一个 resolve 的 Promise[]
const res = Promise.allSettled([p1,p2])
// 返回结果为一个 resolve || reject 的 Promise[]
// 当全部 resolve 时,res2才为 resolve
const res2 = Promise.all([p1,p2])
|
字符串扩展-批量提取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
const str = `
<ul>
<li>
<a>肖申克的的救赎</a>
<p>上映日期:1994-09-10</p>
</li>
<li>
<a>阿甘正传</a>
<p>上映日期:1994-07-10</p>
</li>
</ul>
`
const reg = /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>/gs
const result = str.matchAll(reg)
const arr = [...result]
// [[], []]
|
可选链操作符-?.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
const myConfig = {
db: {
type: 'mysql',
host: '127.0.0.1',
params: {
username: 'root',
password: 'root',
},
}
}
const addConfig = config => {
const username = config.db.params.username
console.log(username)
}
addConfig() // 出错 can't read property of undefined(read 'db')
/* 以下写法可避免这种错误 */
const addConfig2 = config => {
const username = config?.db?.params?.username
console.log(username)
}
addConfig2() // 不出错, 打印 undefined
addConfig2(myConfig) // 打印 root
|
动态 import
1
2
3
4
5
6
7
|
const btn = document.getElementById('btn')
btn.onclick = () => {
import('./hello.js').then(module => {
})
}
|
BigInt 类型
1
2
3
4
5
6
7
|
const maxInt = number.MAX_SAFE_INTEGER // 9007199254740991
maxInt += 1 // 9007199254740992
maxInt += 1 // 9007199254740992 无法再加
// 大整型:用于运算,只能和大整型运算
const n = 1n // 定义一个大整型
BigInt(maxInt) + n // 9007199254740993n
|
全局 this-globalThis
浏览器端:指向 window
Node端:指向 global
ES2021
String.prototype.replaceAll()
1
2
3
4
5
|
// 之前
'jxvxscript'.replace(/x/g, 'a');
// 现在
'jxvxscript'.replaceAll('x', 'a');
|
Promise.any
只要其中的一个成功,就返回那个已经成功的 Promise
所有的都失败/拒绝 就返回一个失败的 Promise
1
2
3
4
5
6
7
8
9
|
const promise1 = new Promise((resolve, reject) => reject('我是失败的Promise_1'));
const promise2 = new Promise((resolve, reject) => reject('我是失败的Promise_2'));
const promiseList = [promise1, promise2];
Promise.any(promiseList).then(values=>{
console.log(values);
})
.catch(e=>{
console.log(e);
});
|
运算符扩展
&&=
1
2
3
4
5
|
a = 1;
b = 2;
a &&= b // a=2
// 等价于
a && a = b
|
??=
1
2
3
4
5
|
a = 1;
b = 2;
a ??= b // a=1
// 等价于
a ?? a = b
|
数字分隔符
number
类型的数据,支持 _
分割
1
2
3
|
const num = 100_220_330
// 等价于
const num = 100220330
|
ES2022
Class Field
属性声明
class
定义属性可以不写 constructor
1
2
3
4
5
6
7
8
9
10
|
// 2022 前
class X {
constructor() {
this.a = 123;
}
}
// 现在
class X {
a = 123;
}
|
属性初始化
如果 class 中属性未初始化,默认值为 undefined
公共属性的 get 和 set
公共字段都是通过 Object.defineProperty
创造的,不会触发 class 中定义的 get
set
1
2
3
4
5
6
7
8
9
10
|
class A {
set x(value) { console.log(++value); }
get x() { return 'x' }
}
class B extends A {
x = 1;
}
const b = new B();
b.x; // 1 (并不会返回'x')
b.x = 2; // 控制台不会打印3
|
私有属性
#
开头的属性被认为是私有属性
1
2
3
4
5
6
|
class X {
// private a = 123
#a = 123;
}
const x = new X();
x.#a // Uncaught SyntaxError: Private field '#a' must be declared in an enclosing class
|
正则扩展-indices
执行 String.match()
或 Regexp.exec()
方法时,如果正则表达式选项配置了 /d
修饰符,
- 结果多返回一个
indices
属性,返回匹配字符串的起始位置
和末尾位置
,左闭右开区间
匿名捕获
1
2
3
4
5
|
const re1 = /a+/d;
const s1 = "aaabbb";
const m1 = re1.exec(s1);
m1.indices[0] //[0, 3];
|
具名捕获
1
2
3
4
5
6
|
const re1 = /a+(?<B>b+)/d;
const s1 = "aaabbbccc";
const m1 = re1.exec(s1);
m1.indices[1] //[3, 6];
m1.indices.groups //{ B: [3, 6] };
|
文件顶层使用 await
之前使用 await
需要在 async
声明的函数里,现在可以直接在 .mjs
文件顶层使用
1
2
3
4
5
|
// awaiting.mjs
import { process } from "./some-module.mjs";
const dynamic = import(computedModuleSpecifier);
const data = fetch(url);
export const output = process((await dynamic).default, await data);
|
1
2
3
|
// usage.mjs
import { output } from "./awaiting.mjs";
export function outputPlusValue(value) { return output + value }
|
class 扩展-in操作符
由于 class 私有属性/方法不可访问,只能使用 try catch
判断 class 定义对象的实例是否含有某个私有属性
现在可以使用 in
关键字来直接判断对象中是否存在某个私有字段、私有方法或者getter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class C {
#brand;
#method() {}
get #getter() {}
static isC(obj) {
return #brand in obj && #method in obj && #getter in obj;
}
}
const c = new C();
const d = {};
C.isC(c) // true
C.isC(d) // false
|
.at()
新增的取值方法,可作用于Array
, String
, TypedArray
,接受一个参数
- 参数为正整数
n
,返回第n
个值
- 参数为负整数
n
,返回第倒数n
个值
- 参数为其他值,返回第
1
个值
1
2
3
4
5
6
|
const arr = [1, 2, 3, 4, 5];
arr.at(1) // 2
arr.at(-1) // 5
arr.at(-10) // undefined
arr.at('aaaa') // 1
arr.at() // 1
|
Object.hasOwn
改良版 Object.hasOwnProperty
当使用Object.create(null)
创建一个没有继承 Object.prototype
的对象时,hasOwnProperty
方法无法直接使用:
1
2
3
4
|
const object = Object.create(null)
object.hasOwnProperty("foo") // Uncaught TypeError: Object.create(...).hasOwnProperty is not a function
Object.prototype.hasOwnProperty.call(object, 'foo') // false
Object.hasOwn(object, 'foo') // false
|
class 静态代码块
初始化 class 静态属性,可以开辟一个静态代码块,去初始化静态属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
class C {
static x = 3;
static y;
static z;
static {
try {
const obj = doSomethingWith(this.x);
this.y = obj.y;
this.z = obj.z;
}
catch {
this.y = 0;
this.z = 0;
}
}
}
|
Error扩展-错误原因
在 Error
构造函数新增了一个可选参数 cause
,允许我们在实例化 Error
时,将错误原因以参数形式传入,省去了我们自己单独处理的成本
1
2
3
4
5
6
7
8
9
10
11
|
async function doJob() {
const rawResource = await fetch('//domain/resource-a')
.catch(err => {
throw new Error('Download raw resource failed', { cause: err });
});
const jobResult = doComputationalHeavyJob(rawResource);
await fetch('//domain/upload', { method: 'POST', body: jobResult })
.catch(err => {
throw new Error('Upload job result failed', { cause: err });
});
}
|
ES2023
敬请期待……