概览

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

常用特性:

  • let/const
  • 类 class
  • 箭头函数
  • 函数默认参数
  • 模板字符串
  • 解构赋值
  • 扩展操作符
  • 对象属性简写
  • Promise
  • 迭代器
  • 生成器
  • Set/Map
  • 模块化

    let/const

  1. 不允许重复声明;
    2. 块级作用域(局部变量);
    3. 不存在变量提升;

当值为基础数据类型时,这里的值,就是指值本身。当我们试图改变 const 声明的变量时,则会报错。

当值对应的为引用数据类型时,这里说的值,则表示指向该对象的引用。这里需要注意,正因为该值为一个引用,只需要保证引用不变就可以,我们仍然可以改变该引用所指向的对象。

能用const尽量用const,const 声明必须赋值。

  1. let a = null;
  2. a = 20;
  3. const obDev = {
  4. a: 20,
  5. b: 30
  6. }
  7. obDev.a = 30;
  8. console.log(obDev); // Object {a: 30, b: 30}

解构赋值

ES6 允许按照一定模式,从数组对象中提取值,对变量进行赋值,这被称为解构赋值

  1. // 提取值
  2. const props = {
  3. className: 'tiger-button',
  4. loading: false,
  5. clicked: true,
  6. disabled: 'disabled'
  7. }
  8. const { loading, clicked } = props;
  9. // 给一个默认值,当props对象中找不到loading时,loading就等于该默认值
  10. const { loading = false, clicked } = props;
  11. -----------
  12. // 交换变量的值
  13. let x = 1;
  14. let y = 2;
  15. [x, y] = [y, x];
  16. -------------
  17. // 模块解构
  18. const { SourceMapConsumer, SourceNode } = require("source-map");
  19. -------------
  20. // 遍历 Map 结构
  21. const map = new Map();
  22. map.set('first', 'hello');
  23. map.set('second', 'world');
  24. for (let [key, value] of map) {
  25. console.log(key + " is " + value);
  26. }
  27. // first is hello
  28. // 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);

  1. <a name="hZ24y"></a>
  2. ## 箭头函数
  3. 1. 箭头函数的this是静态的,始终指向函数声明时所在作用域下的this的值; <br />2. 不能作为构造实例化对象;<br />3. 不能使用 arguments 变量;
  4. ```javascript
  5. // es5
  6. var fn = function(a, b) {
  7. return a + b;
  8. }
  9. // es6 箭头函数写法,当函数直接被return时,可以省略函数体的括号
  10. const fn = (a, b) => a + b;

函数默认值

允许给函数的参数赋初始值;

  1. ES6的默认值写法:
  2. function add(x = 20, y = 30) {
  3. return x + y;
  4. }
  5. console.log(add());
  6. //在实际开发中给参数添加适当的默认值,
  7. //可以让我们对函数的参数类型有一个直观的认知。
  8. const ButtonGroupProps = {
  9. size: 'normal',
  10. className: 'xxxx-button-group',
  11. borderColor: '#333'
  12. }
  13. export default function ButtonGroup(props = ButtonGroupProps) {
  14. ... ...
  15. }

展开运算符

在ES6中用...来表示展开运算符,它可以将数组方法或者对象进行展开。

  1. const arr1 = [1, 2, 3];
  2. const arr2 = [...arr1, 10, 20, 30];
  3. // 这样,arr2 就变成了[1, 2, 3, 10, 20, 30];

当然,展开对象数据也是可以得到类似的结果

  1. const obj1 = {
  2. a: 1,
  3. b: 2,
  4. c: 3
  5. }
  6. const obj2 = {
  7. ...obj1,
  8. d: 4,
  9. e: 5,
  10. f: 6
  11. }
  12. // 结果类似于 const obj2 = Object.assign({}, obj1, {d: 4})

展开运算符还常常运用在解析结构之中,例如我们在Raect封装组件的时候常常不确定props到底还有多少数据会传进来,就会利用展开运算符来处理剩余的数据。

  1. // 这种方式在react中十分常用
  2. const props = {
  3. size: 1,
  4. src: 'xxxx',
  5. mode: 'si'
  6. }
  7. const { size, ...others } = props;
  8. console.log(others)
  9. // 然后再利用暂开运算符传递给下一个元素,再以后封装react组件时会大量使用到这种方式,正在学习react的同学一定要搞懂这种使用方式
  10. <button {...others} size={size} />

展开运算符还用在函数的参数中,来表示函数的不定参。只有放在最后才能作为函数的不定参,否则会报错。

  1. // 所有参数之和
  2. const add = (a, b, ...more) => {
  3. return more.reduce((m, n) => m + n) + a + b
  4. }
  5. console.log(add(1, 23, 1, 2, 3, 4, 5)) // 39

展开运算符的运用可以大大提高我们的代码效率,但是在刚开始使用的时候比较绕脑,掌握好了用起来还是非常爽的,记住这些使用场景,平时在用的时候可以刻意多运用就行了。

对象字面量

ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。

  1. // es6
  2. const person = {
  3. name,
  4. age,
  5. getName() { // 只要不使用箭头函数,this就还是我们熟悉的this
  6. return this.name
  7. }
  8. }
  9. // es5
  10. var person = {
  11. name: name,
  12. age: age,
  13. getName: function getName() {
  14. return this.name;
  15. }
  16. };
  17. //在对象字面量中可以使用中括号作为属性,表示属性名也能是一个变量了。
  18. const name = 'Jane';
  19. const age = 20
  20. const person = {
  21. [name]: true,
  22. [age]: true
  23. }

类Class

  1. 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 } }

  1. - static
  2. ```javascript
  3. class Point {
  4. constructor(x, y) {
  5. this.x = x;
  6. this.y = y;
  7. }
  8. static displayName = "Point";
  9. static distance(a, b) {
  10. const dx = a.x - b.x;
  11. const dy = a.y - b.y;
  12. return Math.hypot(dx, dy);
  13. }
  14. }
  15. const p1 = new Point(5, 5);
  16. const p2 = new Point(10,10);
  17. console.log(Point.displayName);// "Point"
  18. 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 的值总是正确的。

  1. - 继承
  2. ```javascript
  3. class Person {
  4. constructor(name, age) {
  5. this.name = name;
  6. this.age = age;
  7. }
  8. getName() {
  9. return this.name
  10. }
  11. }
  12. // Student类继承Person类
  13. class Student extends Person {
  14. constructor(name, age, gender, classes) {
  15. super(name, age);
  16. this.gender = gender;
  17. this.classes = classes;
  18. }
  19. getGender() {
  20. return this.gender;
  21. }
  22. }

ES2016

Array.prototype.includes

判断数组中是否包含某元素,语法:arr.includes(元素值);

指数运算符(**)

数值求幂 相当于Math.pow()

ES2017

async/await

async 函数:

  • async 函数的返回值为 promise 对象;
  • promise 对象的结果由 async 函数执行的返回值决定;

    await 函数:

  • await 必须写在 async 函数中;

  • await 右侧的表达式一般为 promise 对象;
  • await 返回的是 promise 成功的值;
  • await 的 promise 失败了, 就会抛出异常, 需要通过 try…catch 捕获处理;

对象方法扩展

  1. 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);

  1. **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 通常在合并成一种方法的效率稍微高一些。
  2. ```javascript
  3. var arr1 = [1, 2, 3, 4];
  4. arr1.map(x => [x * 2]);
  5. // [[2], [4], [6], [8]]
  6. arr1.flatMap(x => [x * 2]);
  7. // [2, 4, 6, 8]

image.png

sort稳定性

ES10要求 Array.prototype.sort 为稳定排序。

  1. const students = [
  2. { name: "Alex", grade: 15 },
  3. { name: "Devlin", grade: 15 },
  4. { name: "Eagle", grade: 13 },
  5. { name: "Sam", grade: 14 },
  6. ];
  7. students.sort((firstItem, secondItem) => firstItem.grade - secondItem.grade);
  8. [
  9. { name: "Eagle", grade: 13 },
  10. { name: "Sam", grade: 14 },
  11. { name: "Alex", grade: 15 }, // grade 相同时维持原先的顺序 (稳定排序)
  12. { name: "Devlin", grade: 15 }, // grade 相同时维持原先的顺序 (稳定排序)
  13. ]

Symbol.prototype.description

获取Symbol的描述字符串;

  1. let s = Symbol("哈哈");
  2. console.log(s.description) // 哈哈

trimStart/trimEnd

trimStart() 方法从字符串的开头删除空格。trimLeft() 是此方法的别名。
trimEnd() 方法从一个字符串的末端移除空白字符。trimRight() 是这个方法的别名。

  1. const greeting = ' Hello world! ';
  2. console.log(greeting.trimStart()); //'Hello world! '
  3. console.log(greeting.trimEnd()); //' Hello world!'

Object.fromEntries()
返回以键和值组成的对象(Object.entries()的逆操作),接收类似 Array 、 Map 或者其它可迭代对象。

  1. // Map 转化为 Object
  2. const map = new Map([ ['foo', 'bar'], ['baz', 42] ]);
  3. const obj = Object.fromEntries(map);
  4. console.log(obj); // { foo: "bar", baz: 42 }
  5. // Array 转化为 Object
  6. const arr = [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ];
  7. const obj = Object.fromEntries(arr);
  8. console.log(obj); // { 0: "a", 1: "b", 2: "c" }
  9. // 对象转换
  10. const object1 = { a: 1, b: 2, c: 3 };
  11. const object2 = Object.fromEntries(
  12. Object.entries(object1)
  13. .map(([ key, val ]) => [ key, val * 2 ])
  14. );
  15. console.log(object2);
  16. // { a: 2, b: 4, c: 6 }

ES2020

globalThis

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

  • Browser:顶层对象是window
  • Node:顶层对象是global
  • WebWorker:顶层对象是self
  • 以上三者:通用顶层对象是globalThis

BigInt

可以用在一个整数字面量后面加 n 的方式定义一个 BigInt ,如:10n,或者调用函数BigInt()。

  1. const theBiggestInt = 9007199254740991n;
  2. const alsoHuge = BigInt(9007199254740991);
  3. const hugeString = BigInt("9007199254740991");
  4. typeof 1n === 'bigint'; // true
  5. typeof BigInt('1') === 'bigint'; // true

它在某些方面类似于 Number ,但是也有几个关键的不同点:不能用于 Math 对象中的方法;不能和任何 Number 实例混合运算,两者必须转换成同一种类型。在两种类型来回转换时要小心,因为 BigInt 变量在转换成 Number 变量时可能会丢失精度。

  • BigInt 和 Number 不是严格相等的,但是宽松相等的
  • Number 和 BigInt 可以进行比较。

可选链操作符

是否存在对象属性

  1. // 传统写法
  2. const dbHost = config && config.db && config.db.host;
  3. // 可选链操作符写法
  4. 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”

  1. <a name="sQlF2"></a>
  2. ## 动态import
  3. 标准用法的import导入的模块是静态的,会使所有被导入的模块,在加载时就被编译(无法做到按需编译,降低首页加载速度)。有些场景中,你可能希望根据条件导入模块或者按需导入模块,这时你可以使用动态导入代替静态导入。
  4. 请不要滥用动态导入(只有在必要情况下采用)。静态框架能更好的初始化依赖,而且更有利于静态分析工具和tree shaking发挥作用
  5. 关键字import可以像调用函数一样来动态的导入模块。以这种方式调用,将返回一个 promise。
  6. ```javascript
  7. import('/modules/my-module.js')
  8. .then((module) => {
  9. // Do something with the module.
  10. });
  11. // 这种使用方式也支持 await 关键字。
  12. let module = await import('/modules/my-module.js');

引入import()来代替require(),require()是同步加载,import()是异步加载