概览
| 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// es5var 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 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。
// es6const person = {name,age,getName() { // 只要不使用箭头函数,this就还是我们熟悉的thisreturn this.name}}// es5var person = {name: name,age: age,getName: function getName() {return this.name;}};//在对象字面量中可以使用中括号作为属性,表示属性名也能是一个变量了。const name = 'Jane';const age = 20const 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```javascriptclass 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 的值总是正确的。
- 继承```javascriptclass 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 通常在合并成一种方法的效率稍微高一些。```javascriptvar 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 转化为 Objectconst map = new Map([ ['foo', 'bar'], ['baz', 42] ]);const obj = Object.fromEntries(map);console.log(obj); // { foo: "bar", baz: 42 }// Array 转化为 Objectconst 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'; // truetypeof 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。```javascriptimport('/modules/my-module.js').then((module) => {// Do something with the module.});// 这种使用方式也支持 await 关键字。let module = await import('/modules/my-module.js');
引入import()来代替require(),require()是同步加载,import()是异步加载
