前端基础:想知道 ES6-ES12 这五年是怎么过的么?

325 阅读7分钟

前言

ES2015-ES2020(ES6-ES12)新特性,还在只关注ES6么,ES6是2015年推出,现在都2021年底了,你知道ES这五年是怎么过的么,你造么?

20190817024116787.png

本文我替ES讲讲它的故事

20190817024256132.jpg

ES6(2015)

ES6 我按照 两个分类来方便大家记忆:
1. 使javaScrip更健壮的特性
2. 提升开发体验的语法糖

使javaScrip更健壮的特性

1. 类(class)

    用来属性和方法的集合,class 的写法 有构造函数,
    可设置静态方法、类的继承(extend)、super() 来调用父类。    

2. 模块化(exprot、improt)

    导出可以设置默认导出、可导出多个
    扩展:import defaultExprot, {XXX,XXX} from 'XXX'  这样可以同时导入默认模块的和具名模块

3. Map、Set

    Map: key、value 键值对来设置
     方法:有set(kay,value),has(kay),size,delete(key),entrise()
     遍历: foreach、of
     特点:map 和 object 的不同 map key 可以不用字符串
     
    Set: 类似数组 但是元素没有重复值
     方法:有add(value),has(value),size,delete(value),clear()
     kes(),values(),
     遍历: foreach、of
     特点:元素不可重复、可用于数组去重
     扩展:通过Array.from() 将set转换为数组
     
    其他:
     WeakMap:存放的key不能是基本类型,不可遍历,没有size属性
     WeakSet: 存放的元素不能是基本类型,不可遍历,没有size属性       

4. let、const

let 类型不提升、const 常量      
思考: const 如何使对象内部的值也不被改变

5. promise

实例的方法:
    .then(), .catch(), finally()
    
原型的方法:
    .all([p1,p2,p3]) // 有一个失败全都失败
    .race([p1,p2,p3]) // 有一个率先改变状态就率先改变状态
    .allSettled([p1,p2,p3]) // 等所有的都返回结果、无论成功还是失败
    .resolve() 
    .reject()  

6. Symbol()

    独一无二的值,一般用于对象的key, 防止相同名称会报错

7. 对象新增方法

    Object.is() // 类似于 == 判断,但和== 不同的是 NaN == NaN 是 true
    Object.assign({},{}) // 对象合并、是浅拷贝
    
    其他:Object.values()、Object.keys()、Object.entrise()

提升开发体验的语法糖

1. 箭头函数

    ()=> {} , 注意:this指向问题

2. 解构赋值

    数组的解构赋值、对象的解构赋值 (可设置默认值、可解构字符串)
    
    举例:
    const [a,b,c,d = 40] = [10,20,30]
    const [a,,c] = [10,20,30]
    const {a,b,c} = {a: 10, b:20, c:30}
    const {a,c} = {a: 10, b:20, c:30}
    const {a,b,c} = 'string'

3. 扩展操作符(...)

    (...) 展开数组、展开对象、展开字符串
    举例:
    let arrClone = [...arr]
    let objClone = { ...obj }
    let strArr = [...'hello']

4. 函数参数默认值

    function myFunc (name='gby', age='60') {...}

5. 模板字符串(``)

    模板字符串中${ } 不仅可以放变量 还可以进行
    `你好${name}` ,  `${a}乘以${b}等于${a*b}`

以下这几年的更新我觉得按年份来记比较好记ES2016-ES2020,按版本不太好记忆

ES7(2016)

1. Array.prototype.includes()

 // 判断数组是否存在某个元素
 let arr = [10, 20 ,30]
 以前是arr.indexOf(10) > -1 这样判断, 现在可以
 arr.includes(10)  // true 、 false

2. 求幂运算符

 举例:
 a的三次方
 a**=3 或者 a = a**3

ES8(2017)

1. Object.values / Object.entries

获取所有值、获取所有的键值对,返回的是数组

2. 字符串的填充(String padding)

'XXX'.padStart('长度',, 元素)
'XXX'.padEnd('长度', 元素)

image.png 扩展: trimStarttrimEnd 去除首/尾空格

3. 函数参数列表和调用中的尾逗号

 // 有些情况下我们的参数需要换行写就会像下面这种:
 function setObj(
     name,
     age
 ) {.....}
 // 如上代码另一个人继续追加代码的时候,git提交的时候就会有如下情况:
 function setObj(
     name,
     age, // 这行是你的修改
     sex, // 这行是你的新增
 ){ ..... } 
 就因为多加个逗号这样在git中这两行都显示是你的提交了,这样会引起不必要的甩锅事件
 
 // 现在可以这么写了, 以逗号结尾,不会影响参数的length
  function setObj(
     name,
     age, // 支持逗号结尾
 ) { ..... }
 

4. Object.getOwnPropertyDescriptors

见名知意:获取自己的属性描述符
语法: Object.getOwnPropertyDescriptor(obj, prop)

举例:
o = { bar: 42 };
d = Object.getOwnPropertyDescriptor(o, "bar");
// d {
//   configurable: true,
//   enumerable: true,
//   value: 42,
//   writable: true
// }

5. async/await

async 定义异步函数
await 执行同步等待

项目中都会经常用到的,如果没用过可以尝试试一下哦,
解决了回调地狱问题,会使我们的代码更简洁,更直观
并且比 * yield 这种方式更语义化

ES9(2018)

Promise.prototype.finally

无论成功与否都会执行,一般于关闭loading或者打开其他定义的事件.
p.finally(function() {
   // 返回状态为(resolved 或 rejected)
});

扩展:
try 也有 finally
try {...} finally {...}

异步迭代

1. for-await-of
for await (const line of readLines(filePath)) {
  console.log(line);
}

2. while await
async function* readLines(path) {
  let file = await fileOpen(path);

  try {
    while (!file.EOF) {
      yield await file.readLine();
    }
  } finally {
    await file.close();
  }
}

扩展:forEach下执行异步等待是没效果的

ES10(2019)

可选catch绑定

// catch 是一个函数的形式(带括号)
try {
  // try to use a web feature which may not be implemented
} catch (unused) {
  // fall back to a less desirable web feature with broader support
}

// 允许catch省略绑定及其周围的括号(不带括号)
try {
   // 
} catch {
  // 
}

Object.fromEntries

将entries格式的元素转换成对象。

obj = Object.fromEntries([['a', 0], ['b', 1]]); // { a: 0, b: 1 }
map = new Map([ [ 'a', 1 ], [ 'b', 2 ], [ 'c', 3 ] ]);
obj = Object.fromEntries(map);

trimStart / trimEnd

str.trimEnd();  // 去除尾空格
str.trimStart(); // 去除首空格

Array.prototype.{flat,flatMap}

听说过数组扁平化么?

将 [[1,[2,3]],[[4,5],6],[7,8,9]] 转化为 [1,2,3,4,5,6,7,8,9]
也就是将多维矩阵转成一维数组

举例:
let arr1=[1,2,['a','b','c',['e','f']],3]; 
arr1.flat() // [1, 2, 'a', 'b', 'c', ['e','f'], 3]
arr1.flat(3) // [1, 2, 'a', 'b', 'c', 'e', 'f', 3]
arr1.flat(Infinity) //[1, 2, 'a', 'b', 'c', 'e', 'f', 3]
flat默认相当于2, 传递的数字就是按几维格式展开

flatMap()方法对原数组的每个成员执行一个函数(相当于执行`Array.prototype.map()`),
然后对返回值组成的数组执行`flat()`方法。该方法返回一个新数组,不改变原数组。

[2,3,4].flatMap((x)=>[x, x *2]) // [2, 4, 3, 6, 4, 8] // 相当于 [[2, 4], [3, 6], [4, 8]].flat()
[1,2,3,4].flatMap(x =>[[x *2]])// [[2], [4], [6], [8]]// 相当于 [[[2]], [[4]], [6]], [6]], [8]]].flat()

ES11(2020)

动态import

标准用法的import导入的模块是静态的,会使所有被导入的模块,在加载时就被编译(无法做到按需编译,降低首页加载速度)。有些场景中,你可能希望根据条件导入模块或者按需导入模块,这时你可以使用动态导入代替静态导入。
请不要滥用动态导入(只有在必要情况下采用)。静态框架能更好的初始化依赖,而且更有利于静态分析工具和tree shaking发挥作用

关键字import可以像调用函数一样来动态的导入模块。以这种方式调用,将返回一个 `promise`import('/modules/my-module.js')
  .then((module) => {
    // Do something with the module.
  });
  
这种使用方式也支持 `await` 关键字。
let module = await import('/modules/my-module.js');

BigInt

新增基本类型 BigInt, 用于对超大数进行运算

创建 bigint 的方式有两种:在一个整数字面量后面加 n 或者调用 BigInt 函数,该函数从字符串、数字等中生成 bigint。

const bigint = 9007199254740991
const sameBigint = BigInt("9007199254740991");
const bigintFromNumber = BigInt(10); // 与 10n 相同

更多详情可见MDN BigInt
扩展:其他基本类型

Promise.allSettled

allSettled // 都稳定下来
都状态更改无论成功或是失败时才算完成

空合并运算符

就是短路或。

const response = {
  settings: {
    nullValue: null,
    height: 400,
    animationDuration: 0,
    headerText: '',
    showSplashScreen: false
  }
};

const undefinedValue = response.settings.undefinedValue || 'some other default'; // result: 'some other default'
const nullValue = response.settings.nullValue || 'some other default'; // result: 'some other default'

ES12(2021)

String.prototype.replaceAll

以前使用替换的时候, 如果想要全局替换需要用正则 /g 进行全部的替换,是不是麻烦,现在js也可以使用replaceAll了。

'1+2+3+4'.replace(/\+/g, '-') // '1-2-3-4'

replaceAll:

'1+2+3+4'.replaceAll('+', '-') // '1-2-3-4'

可选链调用

let street = user.address?.street 
// 当 user.address 存在的时候再去找street这样避免没有address时在继续获取street会报错的情况
let fooValue = myForm.querySelector('input[name=foo]')?.value 
// 同理

Promise.any

  • 只要其中的一个 promise 成功,就返回那个已经成功的 promise
  • 如果可迭代对象中没有一个 promise 成功(即所有的 promises 都失败/拒绝),就返回一个失败的
  • 目前是实验性的,不推荐使用

参考链接

ES6犀牛书
ES规范社区