概览
ECMAScript | 发布时间 | 新增特性 |
---|---|---|
ECMAScript 2009(ES5) | 2009年11月 | 扩展了Object、Array、Function等功能 |
ECMAScript 2015(ES6) | 2015年6月 | class、module、箭头函数、默认参数、解构赋值等 |
ECMAScript 2016(ES7) | 2016年3月 | includes、指数操作符 |
ECMAScript 2017(ES8) | 2017年6月 | async/await、Object.values、Object.entries等 |
ECMAScript 2018(ES9) | 2018年 | for-await-of、Promise.prototype.finally、正则表达式优化等 |
ECMAScript 2019(ES10) | 2019年 | flat、flatMap、trimStart、trimEnd、fromEntries等 |
ECMAScript 2020(ES11) | 2020年 | BigInt、Promise.AllSettled、matchAll等 |
ES6
常用特性:
- 不允许重复声明;
2. 块级作用域(局部变量);
3. 不存在变量提升;
当值为基础数据类型时,这里的值,就是指值本身。当我们试图改变 const 声明的变量时,则会报错。
当值对应的为引用数据类型时,这里说的值,则表示指向该对象的引用。这里需要注意,正因为该值为一个引用,只需要保证引用不变就可以,我们仍然可以改变该引用所指向的对象。
能用const
尽量用const
,const 声明必须赋值。
let a = null;
a = 20;
const obDev = {
a: 20,
b: 30
}
obDev.a = 30;
console.log(obDev); // Object {a: 30, b: 30}
解构赋值
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构赋值;
// 提取值
const props = {
className: 'tiger-button',
loading: false,
clicked: true,
disabled: 'disabled'
}
const { loading, clicked } = props;
// 给一个默认值,当props对象中找不到loading时,loading就等于该默认值
const { loading = false, clicked } = props;
-----------
// 交换变量的值
let x = 1;
let y = 2;
[x, y] = [y, x];
-------------
// 模块解构
const { SourceMapConsumer, SourceNode } = require("source-map");
-------------
// 遍历 Map 结构
const map = new Map();
map.set('first', 'hello');
map.set('second', 'world');
for (let [key, value] of map) {
console.log(key + " is " + value);
}
// first is hello
// second is world
模板字符串
模板字符串(template string)是增强版的字符串,用反引号(`)标识,特点:
- 字符串中可以出现换行符;
- 可以使用 ${xxx} 形式引用变量;
``javascript // es6 const a = 20; const b = 30; const string =
${a}+${b}=${a+b}`;
// es5 var a = 20; var b = 30; var string = a + “+” + b + “=” + (a + b);
<a name="hZ24y"></a>
## 箭头函数
1. 箭头函数的this是静态的,始终指向函数声明时所在作用域下的this的值; <br />2. 不能作为构造实例化对象;<br />3. 不能使用 arguments 变量;
```javascript
// es5
var fn = function(a, b) {
return a + b;
}
// es6 箭头函数写法,当函数直接被return时,可以省略函数体的括号
const fn = (a, b) => a + b;
函数默认值
允许给函数的参数赋初始值;
ES6的默认值写法:
function add(x = 20, y = 30) {
return x + y;
}
console.log(add());
//在实际开发中给参数添加适当的默认值,
//可以让我们对函数的参数类型有一个直观的认知。
const ButtonGroupProps = {
size: 'normal',
className: 'xxxx-button-group',
borderColor: '#333'
}
export default function ButtonGroup(props = ButtonGroupProps) {
... ...
}
展开运算符
在ES6中用...
来表示展开运算符,它可以将数组方法或者对象进行展开。
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 10, 20, 30];
// 这样,arr2 就变成了[1, 2, 3, 10, 20, 30];
当然,展开对象数据也是可以得到类似的结果
const obj1 = {
a: 1,
b: 2,
c: 3
}
const obj2 = {
...obj1,
d: 4,
e: 5,
f: 6
}
// 结果类似于 const obj2 = Object.assign({}, obj1, {d: 4})
展开运算符还常常运用在解析结构之中,例如我们在Raect封装组件的时候常常不确定props到底还有多少数据会传进来,就会利用展开运算符来处理剩余的数据。
// 这种方式在react中十分常用
const props = {
size: 1,
src: 'xxxx',
mode: 'si'
}
const { size, ...others } = props;
console.log(others)
// 然后再利用暂开运算符传递给下一个元素,再以后封装react组件时会大量使用到这种方式,正在学习react的同学一定要搞懂这种使用方式
<button {...others} size={size} />
展开运算符还用在函数的参数中,来表示函数的不定参。只有放在最后才能作为函数的不定参,否则会报错。
// 所有参数之和
const add = (a, b, ...more) => {
return more.reduce((m, n) => m + n) + a + b
}
console.log(add(1, 23, 1, 2, 3, 4, 5)) // 39
展开运算符的运用可以大大提高我们的代码效率,但是在刚开始使用的时候比较绕脑,掌握好了用起来还是非常爽的,记住这些使用场景,平时在用的时候可以刻意多运用就行了。
对象字面量
ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。
// es6
const person = {
name,
age,
getName() { // 只要不使用箭头函数,this就还是我们熟悉的this
return this.name
}
}
// es5
var person = {
name: name,
age: age,
getName: function getName() {
return this.name;
}
};
//在对象字面量中可以使用中括号作为属性,表示属性名也能是一个变量了。
const name = 'Jane';
const age = 20
const person = {
[name]: true,
[age]: true
}
类Class
- class 声明类,constructor 定义构造函数初始化;
2. extends 继承父类;
3. super 调用父级构造方法;
4. static 定义静态方法和属性;
5 父类方法可以重写
https://segmentfault.com/a/1190000021285915
https://zh.javascript.info/class ```javascript // ES5 // 构造函数 function Person(name, age) { this.name = name; this.age = age; }
// 原型方法 Person.prototype.getName = function() { return this.name }
// ES6 class Person { constructor(name, age) { // 构造函数 this.name = name; this.age = age; }
getName() { // 原型方法 return this.name } }
- static
```javascript
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
static displayName = "Point";
static distance(a, b) {
const dx = a.x - b.x;
const dy = a.y - b.y;
return Math.hypot(dx, dy);
}
}
const p1 = new Point(5, 5);
const p2 = new Point(10,10);
console.log(Point.displayName);// "Point"
console.log(Point.distance(p1, p2));// 7.0710678118654755
- 字段声明 ```javascript // 公有字段声明 class Rectangle { height = 0; width; constructor(height, width) { this.height = height; this.width = width; } }
// 解决丢失 this class Button { constructor(value) { this.value = value; } click = () => { alert(this.value); } }
let button = new Button(“hello”);
setTimeout(button.click, 1000); // hello
类字段 click = () => {…} 是基于每一个对象被创建的, 在这里对于每一个 Button 对象都有一个独立的方法, 在内部都有一个指向此对象的 this。 我们可以把 button.click 传递到任何地方, 而且 this 的值总是正确的。
- 继承
```javascript
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
getName() {
return this.name
}
}
// Student类继承Person类
class Student extends Person {
constructor(name, age, gender, classes) {
super(name, age);
this.gender = gender;
this.classes = classes;
}
getGender() {
return this.gender;
}
}
ES2016
Array.prototype.includes
判断数组中是否包含某元素,语法:arr.includes(元素值);
指数运算符(**)
ES2017
async/await
async 函数:
- async 函数的返回值为 promise 对象;
promise 对象的结果由 async 函数执行的返回值决定;
await 函数:
await 必须写在 async 函数中;
- await 右侧的表达式一般为 promise 对象;
- await 返回的是 promise 成功的值;
- await 的 promise 失败了, 就会抛出异常, 需要通过 try…catch 捕获处理;
对象方法扩展
- Object.values()方法:返回一个给定对象的所有可枚举属性值的数组;
2. Object.entries()方法:返回一个给定对象自身可遍历属性 [key,value] 的数组;
3. Object.getOwnPropertyDescriptors()该方法:返回指定对象所有自身属性的描述对象;ES2018
扩展运算符
Rest 参数与 spread 扩展运算符在 ES6 中已经引入,不过 ES6 中只针对于数组,在 ES9 中为对象提供了像数组一样的 rest 参数和扩展运算符
正则扩展
略
ES2019
flat/flatMap
flat(depth) 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
- depth 认值为 1。 ```javascript const arr = [1,2,3,[4,5],6,7]; console.log(arr.flat()); //[1, 2, 3, 4, 5, 6, 7]
const arr3 = [1,2,3,4,5]; const result1 = arr3.flatMap(item => [item * 10]); console.log(result1);
**flatMap()** 方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。它与 [map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) 连着深度值为1的 [flat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat) 几乎相同,但 flatMap 通常在合并成一种方法的效率稍微高一些。
```javascript
var arr1 = [1, 2, 3, 4];
arr1.map(x => [x * 2]);
// [[2], [4], [6], [8]]
arr1.flatMap(x => [x * 2]);
// [2, 4, 6, 8]
sort稳定性
ES10要求 Array.prototype.sort 为稳定排序。
const students = [
{ name: "Alex", grade: 15 },
{ name: "Devlin", grade: 15 },
{ name: "Eagle", grade: 13 },
{ name: "Sam", grade: 14 },
];
students.sort((firstItem, secondItem) => firstItem.grade - secondItem.grade);
[
{ name: "Eagle", grade: 13 },
{ name: "Sam", grade: 14 },
{ name: "Alex", grade: 15 }, // grade 相同时维持原先的顺序 (稳定排序)
{ name: "Devlin", grade: 15 }, // grade 相同时维持原先的顺序 (稳定排序)
]
Symbol.prototype.description
获取Symbol的描述字符串;
let s = Symbol("哈哈");
console.log(s.description) // 哈哈
trimStart/trimEnd
trimStart() 方法从字符串的开头删除空格。trimLeft() 是此方法的别名。
trimEnd() 方法从一个字符串的末端移除空白字符。trimRight() 是这个方法的别名。
const greeting = ' Hello world! ';
console.log(greeting.trimStart()); //'Hello world! '
console.log(greeting.trimEnd()); //' Hello world!'
Object.fromEntries()
返回以键和值组成的对象(Object.entries()的逆操作),接收类似 Array 、 Map 或者其它可迭代对象。
// Map 转化为 Object
const map = new Map([ ['foo', 'bar'], ['baz', 42] ]);
const obj = Object.fromEntries(map);
console.log(obj); // { foo: "bar", baz: 42 }
// Array 转化为 Object
const arr = [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ];
const obj = Object.fromEntries(arr);
console.log(obj); // { 0: "a", 1: "b", 2: "c" }
// 对象转换
const object1 = { a: 1, b: 2, c: 3 };
const object2 = Object.fromEntries(
Object.entries(object1)
.map(([ key, val ]) => [ key, val * 2 ])
);
console.log(object2);
// { a: 2, b: 4, c: 6 }
ES2020
globalThis
globalThis:作为顶层对象,指向全局环境下的this
- Browser:顶层对象是window
- Node:顶层对象是global
- WebWorker:顶层对象是self
- 以上三者:通用顶层对象是globalThis
BigInt
可以用在一个整数字面量后面加 n 的方式定义一个 BigInt ,如:10n,或者调用函数BigInt()。
const theBiggestInt = 9007199254740991n;
const alsoHuge = BigInt(9007199254740991);
const hugeString = BigInt("9007199254740991");
typeof 1n === 'bigint'; // true
typeof BigInt('1') === 'bigint'; // true
它在某些方面类似于 Number ,但是也有几个关键的不同点:不能用于 Math 对象中的方法;不能和任何 Number 实例混合运算,两者必须转换成同一种类型。在两种类型来回转换时要小心,因为 BigInt 变量在转换成 Number 变量时可能会丢失精度。
- BigInt 和 Number 不是严格相等的,但是宽松相等的
- Number 和 BigInt 可以进行比较。
可选链操作符
是否存在对象属性
// 传统写法
const dbHost = config && config.db && config.db.host;
// 可选链操作符写法
const dbHost = config?.db?.host;
Promise.allSettled
Promise.allSettled()方法返回一个在所有给定的promise都已经fulfilled或rejected后的promise,并带有一个对象数组,每个对象表示对应的promise结果。
- 入参:具有Iterator接口的数据结构
- 成功:成员包含 status 和 value,status 为 fulfilled,value 为返回值
- 失败:成员包含 status 和 reason,status 为 rejected,reason 为错误原因 ```javascript const promise1 = Promise.resolve(3); const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, ‘foo’)); const promises = [promise1, promise2];
Promise.allSettled(promises). then((results) => results.forEach((result) => console.log(result.status)));
// expected output: // “fulfilled” // “rejected”
<a name="sQlF2"></a>
## 动态import
标准用法的import导入的模块是静态的,会使所有被导入的模块,在加载时就被编译(无法做到按需编译,降低首页加载速度)。有些场景中,你可能希望根据条件导入模块或者按需导入模块,这时你可以使用动态导入代替静态导入。
请不要滥用动态导入(只有在必要情况下采用)。静态框架能更好的初始化依赖,而且更有利于静态分析工具和tree shaking发挥作用
关键字import可以像调用函数一样来动态的导入模块。以这种方式调用,将返回一个 promise。
```javascript
import('/modules/my-module.js')
.then((module) => {
// Do something with the module.
});
// 这种使用方式也支持 await 关键字。
let module = await import('/modules/my-module.js');
引入import()来代替require(),require()是同步加载,import()是异步加载