ES6+那些事(持续更新)

354 阅读6分钟

写在前面

对象与数组的解构

数组的解构

const [a, b, c] = [1, 2, 3] // a 1 b 2 c3

const [a,,c] = [1,2,3] // a 1  c 3

对象的解构

const person = { name: 'huangyanting', age: 3 }

const { name, age } = person
// name huangyanting age 3

像此处的 name 这个变量,嵌套了足足四层,此时如果我们仍然尝试老方法来提取它:

显然是不奏效的,因为 school 这个对象本身是没有 name 这个属性的,name 位于 school 对象的“儿子的儿子”对象里面。要想把 name 提取出来,一种比较笨的方法是逐层解构:

const school = {
    classes: {
        stu: {
            name: 'Bob',
            age: 24,
        }
    }
}

const { classes } = school 
const { stu } = classes 
const { name } = stu name // 'Bob'

但是还有一种更标准的做法,我们可以用一行代码来解决这个问题:

const { classes: { stu: { name } }} = school 

name // 'Bob'

对象扩展运算

对象中的扩展运算符(…)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中。举个例子:

    const me = {
        name: 'tingGe',
        age: 3
    }

    const meCopy = { ...me }

    meCopy // {name: "tingGe", age: 3}

这里的 …me 其实就等价于下面这种写法:

Object.assign({}, me)

数组扩展运算

在数组中,扩展运算可以将一个数组转为用逗号分隔的参数序列。举个例子:

console.log(...['haha', 'hehe', 'xixi']) 
// haha hehe xixi
function mutiple(x, y) { 
     return x*y 
} 
const arr = [2, 3] 
mutiple(...arr) // 6

合并两个数组

const arr1 = [1, 2, 3, 4] 
const arr2 = [5, 6, 7, 8]

如何把两个数组合并到一个数组里去。这套题可能有很多种解题姿势,但是最好看的一种姿势莫过于用我们的扩展符:

const newArr = [...arr1, ...arr2]

rest 参数 相对更为熟悉的一些扩展符的操作。但是扩展符还有它的另一面,当它被用在函数形参上时,它还可以把一个分离的参数序列整合成一个数组:

function mutiple(...args) {
    let result = 1;
    for (var val of args) {
        result *= val;
    }
    return result;
}

mutiple(1, 2, 3, 4) // 24
function mutiple(...args) { 
   console.log(args) 
}
mutiple(1, 2, 3, 4) // [1, 2, 3, 4]

类数组的转换

类数组对象,ECMA-262对它的定义是:

1.它必须是一个对象 2.它有 length 属性

按照这个标准,只要有 length 属性的对象就是类数组对象:

const book = { 
      name: 'how to read a book',
      age: 10, 
      length: 300 
} // 这是一个类数组对象

类数组对象是指拥有一个 length 属性和若干索引属性的对象

const book = { 
      0: 'how to read a book',
      1: 10, 
      length: 2 
}

其实通常,类似数组的对象也有一些具有整数索引名的属性,不过这并非定义的要求。因此上面两个 book,都可以认为是类数组的对象。

需要注意的是类数组的转换

如何把类数组对象转换为真正的数组?

const arrayLike = { 
         0: 'Bob',
         1: 'Lucy', 
         2: 'Daisy', 
         length: 3
}

ES5中Array原型上的slice方法—— 这个方法如果不传参数的话会返回原数组的一个拷贝,因此可以用此方法转换类数组到数组。

const arr = Array.prototype.slice.call(arrayLike);

Array.from方法——这是 ES6 新增的一个数组方法,专门用来把类数组转为数组:

const arr = Array.from(arrayLike);

扩展运算符——"…"也可以把类数组对象转换为数组,前提是这个类数组对象上部署了遍历器接口。在这个例子中,arrayLike 没有部署遍历器接口,所以这条路走不通。但一些对象,比如函数内部的 arguments 变量(它也是类数组对象),就满足条件,可以用这种方法来转换:

function demo() {
console.log('转换后的 arguments 对象:',[...arguments])
}

demo(1, 2, 3, 4) 
// 转换后的 arguments 对象:[1, 2, 3, 4]

模板语法与字符串处理

let name = 'tingGE'
let career = 'coder'
let hobby = ['coding', 'writing']
let finalString = `
my name is ${name}, 
I work as a ${career} I love ${hobby[0]} 
and ${hobby[1]}`
console.log(finalString);

// my name is tingGE, I work as a coder,
// I love coding and writing

在模板字符串中,空格、缩进、换行都会被保留 模板字符串完全支持“运算”式的表达式,你可以在${}里完成一些计算

更强的方法

除了模板语法外, ES6中还新增了一系列的字符串方法用于提升我们的开发效率,提取其中常用的部分,如下:

存在性判定:在过去,当我们想判断一个字符/字符串是否在某字符串中时,只能用 indexOf > -1 来做。现在 ES6 提供了三个方法:includes、startsWith、endsWith,它们都会返回一个布尔值来告诉你是否存在。

  • includes:判断字符串与子串的包含关系:
const son = 'haha'
const father = 'xixi haha hehe'  
father.includes(son) // true
  • startsWith:判断字符串是否以某个/某串字符开头:
const father = 'xixi haha hehe' 
father.startsWith('haha') // false 
father.startsWith('xixi') // true

自动重复: 我们可以使用 repeat 方法 来使同一个字符串输出多次(被连续复制多次)

const sourceCode = 'repeat for 3 times;'  
const repeated = sourceCode.repeat(3) 
console.log(repeated) 
// repeat for 3 times; repeat for 3 times;  repeat for 3 times;

合并空运算符 ??

假设变量不存在,希望给一个默认值,一般会使用||运算符。但是在javascript中空字符串,0,false都会执行||运算符,ECMAScript2020引入合并空运算符解决该问题,只允许在值为null或undefined时使用默认值。

const name = '';
console.log(name || 'zz'); // zz;
console.log(name ?? 'zz'); // '';

可选链运算符 ?.

业务代码中经常会遇到这样的情况,a对象有个属性b,b也是一个对象有个属性c。

const a = {  
   b: {   
      c: 123,   
      }
}

访问c,经常会写成a.b.c,但是如果b不存在时,就会出错。

ECMAScript2020定义可选链运算符解决该问题,在.之前添加一个?将键名变成可选。

let person = {};
console.log(person?.profile?.age ?? 18); // 18

动态导入import

import('./a.js')返回一个Promise对象。

const a = 123;
export { a };
import('./a.js').then(data => { 
   console.log(data.a); // 123;
});

String.prototype.replaceAll()

为了方便字符串的全局替换,ES2021将支持String.prototype.replaceAll()方法,可以不用写正则表达式就可以完成字符串的全局替换。

'abc555'.replaceAll('5', '2'); // abc222

Promise.allSettled

当处理多个Promise时,特别是当它们相互依赖时,记录每个Promise所发生的事情来调试错误是很有必要的。

通过Promise.allSettled可以创建一个新的Promise,它只在所有传递给它的Promise都完成时返回一个数组,其中包含每个Promise的数据。

const p1 = new Promise((res, rej) => setTimeout(res, 1000));
const p2 = new Promise((res, rej) => setTimeout(rej, 1000));
Promise.allSettled([p1, p2]).then(data => console.log(data));

[ Object { status: "fulfilled", value: undefined},
  Object { status: "rejected", reason: undefined} ]

Promise.all是当多个promise全部成功,或出现第一个失败就会结束。Promise.allSettled是所有都执行完成,无论成功失败。

Promise.any

只要有一个promise是fulfilled时,则返回一个resolved promise;所有的promise都是rejected时,则返回一个rejected promise

Promise.any([ Promise.reject(1), Promise.resolve(2) ])
.then(result => console.log('result:', result)) 
.catch(error => console.error('error:', error)); // result: 2

for of 支持异步迭代

在此之前想要实现异步迭代想要在for of外层嵌套一个async函数

async function () {   
   for (const fn of actions) {      
       await fn();   
   }
}

ES2018提供了一种新的书写方式。

async function() {  
    for await (const fn of actions) {  
       fn();    
    }
}