浅谈 ECMAScript 新特性

image.png

说明:这里仅列举了工作中常用或个人觉得比较好用的新特性,已经部分特性在使用中可能遇到的小问题、特殊场景,仅供参考。

一、ES6(2015)

ES6 参考了 阮一峰 中列举的新特性,结合了工作中的小认知。

1.1、let const

声明的变量/常量只在它所在的代码块内有效。

ES6 规定暂时性死区和 letconst 语句不出现变量提升,主要是为了减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为。

1.2、解构赋值

解构:ES6 语法,从数组和对象中提取值,对变量进行赋值

  • 数组

数组的结构赋值:保证左右两侧结构相同

let [a, b, c] = [1, 2, 3]; // 可以从数组中提取值,按照对应位置,对变量赋值
console.log(`a=${a},b=${b},c=${c}`); // a=1,b=2,c=3

let [foo, [[bar], baz]] = [1, [[2], 3]];
console.log(`foo=${foo},bar=${bar},baz=${baz}`); // foo=1,bar=2,baz=3

let [head, ...tail] = [1, 2, 3, 4];
console.log(head); // head 1
console.log(tail); // tail  [2, 3, 4]

let [x = 1, y = x] = [1, 2]; // 为解构赋值添加默认值
console.log(`x=${x},y=${y}`); // x=1,y=2
复制代码
  • 对象

对象的解构赋值:是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。

let { foo: baz } = { foo: "aaa", bar: "bbb" };
baz // "aaa"
foo // error: foo is not defined
复制代码

foo 匹配的模式
baz 被赋值的变量

  • 字符串

字符串的解构赋值:字符串被转换成了一个类似数组的对象。

const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
复制代码

类数组的对象都有一个共同的属性 length:

let {length : len} = 'hello';
len // 5
复制代码
  • 数值

数值的解构赋值:默认先将其转为对象,取其属性

let {toString: s} = 123;
s === Number.prototype.toString // true
复制代码
  • 布尔值

布尔值的解构赋值:默认先将其转为对象,取其属性

let {toString: s} = true;
s === Boolean.prototype.toString // true
复制代码
  • 函数

函数参数的解构赋值:其本身就是针对数组或者对象等的解构赋值

[[1, 2], [3, 4]].map(([a, b]) => a + b); // [ 3, 7 ]

function move({x = 0, y = 0} = {}) {
    return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]
复制代码

1.3、扩展运算符

  • 扩展运算符用三个点 ... 表示,功能是把数组或类数组对象展开成一系列用逗号隔开的值
let foo = function(a, b, c) { 
    console.log(a); 
    console.log(b); 
    console.log(c); 
} 
let arr = [1, 2, 3]; // 传统写法 
foo(arr[0], arr[1], arr[2]); // 使用扩展运算符 
foo(...arr); // 1 // 2 // 3 

// 数组深拷贝 
let arr2 = arr; 
let arr3 = [...arr]; // 把数组拆开再赋值 
console.log(arr === arr2); // true, 说明arr和arr2指向同一个数组 
console.log(arr === arr3); // false, 说明arr3和arr指向不同数组

// 把一个数组插入另一个数组字面量 
let arr4 = [...arr, 4, 5, 6]; 
console.log(arr4);  // [1, 2, 3, 4, 5, 6] 

// 字符串转数组 
let str = 'hello'; 
let arr5 = [...str]; 
console.log(arr5); // [ 'h', 'e', 'l', 'l', 'o' ]
复制代码
  • Map 和 Set 结构,Generator 函数
let map = new Map([
    [1, 'one'],
    [2, 'two'],
    [3, 'three'],
]);
[...map.keys()]; // [1, 2, 3]

let set = new Set([4, 5, 6]);
[...set]; // [4, 5, 6]

let go = function*() {
    yield 1;
    yield 2;
    yield 3;
};
[...go()]; // [1, 2, 3]
复制代码

1.4、rest 参数

rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用 arguments 对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

// arguments 变量
function sortNumbers() {
  return Array.prototype.slice.call(arguments).sort();
}

// rest 参数
const sortNumbers = (...numbers) => numbers.sort();
复制代码

注意:arguments 是一个类数组的对象,可以使用 Array.prototype.slice.call 将其转为数组。

1.5、字符串

  • 模板字符串

解决了以前繁琐的字符串拼接方式,模板字符串 是增强版的字符串,使用反引号 ` 标识,可以用来定义多行字符串、字符串内嵌入变量等。

const data = {key: 'key'};

// 以前
console.log(
    '这是一个多行文本字符串,' +
    '注意文案内容的换行显示。'
);
console.log('这是字符串中嵌入了变量' + ' ' + data.key);

// 模板字符串
console.log(`
    这是一个多行文本字符串,
    注意文案内容的换行显示。
`);
console.log(`这是字符串中嵌入了变量 ${data.key}`);
复制代码

由于模板字符串的 ${} 中是 js 代码,实际内部就是一个字符串,将会原样输出,加入当前变量在 data 中不存在,则:

console.log(`这是字符串中嵌入了变量 ${data.name}`); // 这是字符串中嵌入了变量 undefined
复制代码
  • includes()

用于判断字符串中是否包含某字符,返回 true/false。支持第二个参数,表示开始搜索的位置。

'Hello world'.includes('ll'); // true
'Hello world'.includes('ll', 5); // false
复制代码
  • startsWith()

用于判断某字符是否在字符串的头部,返回 true/false。支持第二个参数,表示开始搜索的位置。

'Hello world'.startsWith('He'); // true
'Hello world'.startsWith('He', 5); // false
复制代码
  • endsWith()

用于判断某字符是否在字符串的尾部,返回 true/false。支持第二个参数,表示针对前 n 个字符进行搜索。

'Hello world'.endsWith('ld'); // true
'Hello world'.endsWith('ld', 5); // true
复制代码
  • repeat()

用于将原字符串重复 n 次,返回一个新的字符串。

若遇到小数,则只取整数部分
若遇到 0,则为空字符串
若遇到 0-1 之前的小数或者 NaN,则等同于 0
若遇到其他负数或Infinity,则报异常

'na'.repeat(2) // "nana"
'na'.repeat(2.9) // "nana"
'na'.repeat(0) // ""
'na'.repeat(-0.9) // ""
'na'.repeat(NaN) // ""
'na'.repeat(-1); // RangeError
复制代码
  • matchAll()

用于返回一个正则表达式在当前字符串中的所有匹配。

let str = 'aabbcc';
let arr = [...str.matchAll('b')]; // [["b", index: 2, input: "aabbcc", groups: undefined],["b", index: 3, input: "aabbcc", groups: undefined]]
复制代码
  • String.fromCharCode()

用于识别 Unicode 码,返回对应的字符,但是这个方法不能识别码点大于0xFFFF的字符。

String.fromCharCode(0x20BB7); // "ஷ"
复制代码
  • String.fromCodePoint()

用于识别 Unicode 码点大于0xFFFF的字符。

String.fromCodePoint(0x20BB7); // "?"
复制代码

1.6、数值

  • Number.isFinite()

用来判断该数值是否为有限的,而非 Infinity,返回 true/false。针对非数值的参数均返回 false。

Number.isFinite(1); // true
Number.isFinite(Infinity); // false
复制代码
  • Number.isNaN()

用来判断该数值是否为 NaN

Number.isNaN(NaN); // true
Number.isNaN(1); // false
复制代码
  • Number.parseInt()Number.parseFloat()

为了减少全局性的方法,将语言逐步模块化,ES6 将全局的 parseInt()parseFloat(),移植到 Number 对象上了。

parseInt('12.34'); // 12
parseFloat('123.45#'); // 123.45

Number.parseInt('12.34'); // 12
Number.parseFloat('123.45#'); // 123.45
复制代码
  • Number.isInteger()

用来判断该数值是否为整数,返回 true/false。针对非数值的参数均返回 false。

Number.isInteger(25); // true
Number.isInteger(25.0); // true
复制代码
  • Math.trunc()

以前针对数值的 Math,有向上取整和向下取整,显然有一种场景在正数和负数时,无法直接的满足我们仅想取整数部分的需求,而 Math.trunc() 用来去除数值的小数部分,返回整数部分。

针对非数值,其内部会先试用 Number 进行转换
当遇到无法转换成数值的参数,则返回 NaN

Math.trunc(2.9); // 2
Math.trunc('2.9'); // 2
Math.trunc(true); // 1
Math.trunc(NaN); // NaN
复制代码
  • Math.sign()

用来判断一个数到底是正数、负数、还是零。对于非数值,会先将其转换为数值。

参数为正数,返回 +1
参数为负数,返回 -1
参数为 0,返回 0
参数为-0,返回 -0
其他值,返回 NaN

1.7、函数

  • 默认值
// 函数中的参数变量 x y 是默认声明的,在函数体内,不能用 let const 再次声明,否则会报错
function setParam(x, y = 1) {
    console.log(x, y);
}
复制代码

注意:若入参为 undefined,则使用默认值,若为 null 时,则无法使用默认值。

  • 箭头函数

箭头函数使得表达式更加简洁。

// 简洁的语句可以直接返回
[1,2,3].map(x => x * x);

// 使用圆括号包裹
const getTempItem = id => ({ tempId: id, name: 'Temp' });
复制代码

由于其作用域内没有 自己的 this 对象,在定义时绑定了上层作用域中的 this,也就是说箭头函数内部的 this 指向是固定的。

// ES5
function foo() {
    let _this = this;

    setTimeout(function () {
        console.log('id:', _this.id);
    }, 100);
}

// ES6
function foo() {
    setTimeout(() => {
        console.log('id:', this.id);
    }, 100);
}
复制代码

注意:即使箭头函数内嵌套箭头函数,其 this 的指向也只有一个,也就是最外层定义的函数。

function foo() {
    return () => {
        return () => {
            return () => {
                console.log('id:', this.id);
            };
        };
    };
}

let f = foo.call({id: 1});

let t1 = f.call({id: 2})()(); // id: 1
let t2 = f().call({id: 3})(); // id: 1
let t3 = f()().call({id: 4}); // id: 1
复制代码

1.8、数组

  • Array.from()

Array.from 用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。扩展运算符 ... 也可以将某些数据结构转为数组。

// Set、Map
const setArr = new Set([1, 2, 3, 1, 2]); // Set(3) {1, 2, 3}
console.log(Array.from(setArr), [...setArr]); // [1, 2, 3]

// arguments 对象
function foo() {
    let args = Array.from(arguments);
    console.log(args, [...arguments]);
}
foo('name', 'age'); // ['name', 'age']
复制代码
  • findfindIndex

包括三个参数:

value 当前的值
index 当前值的索引
arr 原数组

数组实例的 find 方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为 true 的成员,然后返回该成员。如果没有符合条件的成员,则返回 undefined

[1, 4, -5, -2, 10].find((n) => n < 0); // -5
复制代码

数组实例的 findIndex 方法的用法与 find 方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回 -1

[1, 4, -5, -2, 10].findIndex((n) => n > 4); // 4
[1, 4, -5, -2, 10].findIndex((n) => n > 4); // -1
复制代码

可以接受第二个参数,用来绑定回调函数的this对象。

// 回调函数中的 this 对象指向 person对象
function f(v) {
    return v > this.age;
}
const person = {name: 'John', age: 20};
[10, 12, 26, 15].find(f, person); // 26
复制代码

1.9、对象

  • 元素简写
function foo(x, y) {
    // return {x: x, y: y};
    return {x, y};
}
复制代码
  • 属性遍历

for ... in

for(let key in {name: 'wqjiao', age: 18}) {
    console.log(key);
}
复制代码

Object.keys(obj) -> ES5 中引入的该方法

Object.keys({name: 'wqjiao', age: 18}); // ['name', 'age']
复制代码

Object.getOwnPropertyNames(obj)

Object.getOwnPropertyNames({name: 'wqjiao', age: 18, [Symbol('height')]: 167}); // ['name', 'age']
复制代码

Object.getOwnPropertySymbols(obj)

Object.getOwnPropertySymbols({name: 'wqjiao', age: 18, [Symbol('height')]: 167}); // [Symbol(height)]
复制代码

Reflect.ownKeys(obj)

Reflect.ownKeys({name: 'wqjiao', age: 18, [Symbol('height')]: 167}); // ["name", "age", Symbol(height)]
复制代码
  • 属性遍历的次序

首先遍历所有数值键,按照数值升序排列。
其次遍历所有字符串键,按照加入时间升序排列。
最后遍历所有 Symbol 键,按照加入时间升序排列。

  • Object.is()

用于比较两个值是否严格相等,与 === 的行为基本一致,但是针对 NaN 表示相同的。

Object.is('foo', 'foo'); // true
Object.is({}, {}); // false
Object.is(+0, -0); // false
Object.is(NaN, NaN); // true
复制代码

1.10、Symbol

表示独一无二的值,属于原始数据类型之一

undefined
null
布尔值(Boolean)
字符串(String)
数值(Number)
对象(Object)
Symbol 函数

1.11、Set 和 Map 数据结构

  • Map 结构
const map = new Map([
    [1, 'one'],
    [2, 'two'],
    [3, 'three'],
]);
const keys = [...map.keys()]; // [1, 2, 3]
const values = [...map.values()]; // ['one', 'two', 'three']
map.has(1);
map.get(1);
map.set(4, 'four');
map.delete(1);
复制代码
  • Set 结构
const set = new Set([4, 5, 6]);
set.has(6);
set.add(7);
set.delete(4);
复制代码

1.12、Generator

Generator 函数是 ES6 提供的一种异步编程解决方案,返回一个遍历器对象。

function 关键字与函数名之前存在一个星号
函数体内部使用 yield 表达式
若内部存在多个状态,执行下一步时,必须调用 next 方法,当 done 属性值为 true 时表示遍历已经结束

function* helloWorldGenerator() {
    console.log(111);
    yield 'hello';
    console.log(222);
    yield 'world';
    console.log(333);
    return 'ending';
}
let hw = helloWorldGenerator();

hw.next();
hw.next();
hw.next();
hw.next(); // {value: undefined, done: true}
复制代码

1.13、类 class

class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }

    toString() {
        return '(' + this.x + ', ' + this.y + ')';
    }
}
复制代码

1.14、模块化

export const a = '';
export function a() {};
export {
    a: '',
    b: '',
};
export default a;
export default class;
复制代码

二、ES7(2016)

2.1、includes

表示某个数组中是否包含给定的值,返回 true/false,与字符串中的 includes 类似。

value 搜索的目标值
startIndex 搜索的起始位置,默认为 0

[1, 2, 3].includes(3); // true
[1, 2, 3].includes(3, 3); // false
复制代码

2.2、指数运算符

指数运算符 ** 的一个特点是右结合。

// 不使用指数操作符:递归或Math.pow
Math.pow(2, 2); // 4

// 使用指数操作符
2 ** 2 // 4
2 ** 3 // 8
2 ** 3 ** 2 // 相当于 2 ** (3 ** 2) = 512
复制代码

指数运算符可以与等号结合,形成一个新的赋值运算符(**=)。

// 等同于 a = a * a;
let a = 1.5;
a **= 2;

// 等同于 b = b * b * b;
let b = 4;
b **= 3;
复制代码

三、ES8(2017)

3.1、async/await

async 就是 Generator 函数的语法糖,本质上是将 Generator 函数的星号(*)替换成 async,将 yield 替换成 await,返回一个 Promise 对象。

const gen = function* () {
    const f1 = yield readFile('/etc/fstab');
    const f2 = yield readFile('/etc/shells');
    console.log(f1.toString());
    console.log(f2.toString());
};

const asy = async function () {
    const f1 = await readFile('/etc/fstab');
    const f2 = await readFile('/etc/shells');
    console.log(f1.toString());
    console.log(f2.toString());
};
复制代码
async function f() {
    const a = await new Promise(function (resolve, reject) {
        throw new Error('出错了');
    });
    console.log('a:',a)
}

f()
.then(v => console.log('v:', v))
.catch(e => console.log('e:', e))
复制代码

Promise 对象内抛出异常时,会执行 catch 方法,而此时如果没有使用 catch,则页面会出现异常,可以使用 try ... catch 代码块包裹。

3.2、Object.values()

作用是返回一个对象中值的数组,返回数组的成员顺序,与属性的遍历中一致。

const obj = { foo: 'bar', baz: 42 };
Object.values(obj); // ['bar', 42]
复制代码

3.3、Object.entries()

Object.entries() 函数返回一个给定对象自身可枚举属性的键值对的数组。

const obj = {a: 111, b: 222, c: 333};
// 使用前
Object.keys(obj).forEach(key => {
	console.log('key:'+key+' value:'+obj[key]);
})

// 使用时
for(let [key, value] of Object.entries(obj)){
	console.log(`key: ${key} value:${value}`);
}
复制代码

3.4、padStart()、padEnd()

引入了字符串补全长度的功能,如果某个字符串不够指定长度,会在头部或尾部补全。 padStart()用于头部补全,padEnd()用于尾部补全。

  • 参数

第一个参数 -> 字符串补全生效的最大长度
第二个参数 -> 用来补全的字符串

'x'.padStart(5, 'ab') // 'ababx'
'x'.padEnd(5, 'ab') // 'xabab'
复制代码
  • 如果原字符串的长度,等于或大于最大长度,则字符串补全不生效,返回原字符串。
'xxx'.padStart(2, 'ab') // 'xxx'
'xxx'.padEnd(2, 'ab') // 'xxx'
复制代码
  • 如果省略第二个参数,则默认使用空格补全长度。
'x'.padStart(5) // '    x'
'x'.padEnd(5) // 'x    '
复制代码
  • 实际用途:数值补全指定位数、提示字符串格式
'1'.padStart(10, '0') // "0000000001"
'12'.padStart(10, '0') // "0000000012"
'123456'.padStart(10, '0') // "0000123456"

'12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12"
'09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"
复制代码

3.5、函数参数的尾逗号

支持函数的最后一个参数有尾逗号。

function clownsEverywhere(
    param1,
    param2,
) {
    // ...
}
clownsEverywhere(
  'foo',
  'bar',
);

// 或者
const params = {
    id: '111',
    name: 'wqjiao',
}
复制代码

针对版本迭代的代码来说,函数参数的尾逗号是必要的,若以上代码增加第三个参数,势必会在原来的参数后面先添加一个逗号,修改记录中就会显示原来的参数位置发生了变动。

3.6、Object.getOwnPropertyDescriptors()

Object.getOwnPropertyDescriptors() 函数用来获取一个对象的所有自身属性的描述符,如果没有任何自身属性,则返回空对象。

const obj = {
    name: 'Jine',
    get age() { return '18' }
};
Object.getOwnPropertyDescriptors(obj);
复制代码

3.7、SharedArrayBuffer

SharedArrayBuffer 对象用来表示一个通用的,固定长度的原始二进制数据缓冲区,类似于 ArrayBuffer 对象,它们都可以用来在共享内存上创建视图。与 ArrayBuffer 不同的是,SharedArrayBuffer 不能被分离。

3.8、Atomics

Atomics 对象提供了一组静态方法用来对 SharedArrayBuffer 对象进行原子操作。

四、ES9(2018)

4.1、异步遍历器

当异步方法遇到数组遍历时,如何等待多个异步过程执行完成。

let data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];

// 异步操作
async function doSomething(id) {
    return window.fetch(
        `http://yapi.devops.guchele.com/mock/288/rpm-web/category/child/${id}`,
        {
            headers: {
                Accept: 'application/json',
                'Content-Type': 'application/json; charset=utf-8',
            },
            mode: 'cors',
            redirect: 'follow',
            referrer: 'no-referrer',
            method: 'GET',
        }
    )
        .then(response => {
            return response.json();
        })
        .then(res => {
            console.log(`**${id}:res**`, res);
            return res;
        });
}
复制代码
  • 刚接触时可能会这样尝试,但结果并没有按预期执行
function getRequest() {
    let all = data.map(async item => {
        let res = await doSomething(item);
        return {id: item, res};
    });

    console.log('complete:', all);
}

async function getRequest() {
    let all = [];

    for await (let item of data) {
        doSomething(item);
    }

    console.log('complete');
}
复制代码

注意:遍历本身依旧保持同步,并在内部异步函数之前全部调用完成。

  • for...of
async function getRequest() {
    let all = [];

    for (let item of data) {
        let res = await doSomething(item);
        all.push({id: item, res});
    }

    console.log('complete:', all);
}
复制代码
  • Promise.all
async function getRequest() {
    let all = await Promise.all(
        data.map(async item => {
            let res = await doSomething(item);
            return {id: item, res};
        })
    );

    console.log('complete:', all);
}
复制代码

4.2、Promise.finally()

一个 Promise 调用链要么成功到达最后一个 .then(),要么失败触发 .catch()。在某些情况下,你想要在无论 Promise 运行成功还是失败,运行相同的代码,例如清除,删除对话,关闭数据库连接等。.finally() 允许你指定最终的逻辑。

function doSomething() {
    doSomething1()
        .then(doSomething2)
        .then(doSomething3)
        .catch(err => {
            console.log(err);
        })
        .finally(() => {
            // finish here!
        });
}
复制代码

4.3、针对一些新特性做了扩展及修改

  • 模板字符串,针对不合法的参数,显示 undefined,去除报错等

  • rest 参数和扩展运算符,支持除数组之外的其他类型:对象

  • 正则表达式相关的修改,如修饰符、后行断言等

    先行断言: x 必须在 y 前面才能匹配 /x(?=y)/,如百分值 /\d+(?=%)/.exec('100%')[0] = 100
    后行断言: x 必须在 y 后面才能匹配 /(?<=y)x/,如货币价格 (?<=\$)\d+/.exec('$100')[0] = '100'

五、ES10(2019)

5.1、trimStart()、trimEnd()

我们可能都用过 trim()(ES5 中引入的该方法),表示消除字符串首尾的空格,这里引入了以下两种消除空格的方法。

  • trimStart() 用于消除字符串头部的空格,且保留尾部的空格,存在一个别名 trimLeft()
'  trim  '.trimStart(); // "trim  "
'  trim  '.trimLeft(); // "trim  "
复制代码
  • trimEnd() 用于消除字符串尾部的空格,且仅消除尾部保留头部,存在一个别名 trimRight()
'  trim  '.trimEnd(); // "  trim"
'  trim  '.trimRight(); // "  trim"
复制代码

详见 github.com/tc39/propos…

5.2、flat()、flatMap()

  • flat()

数组降维,将原多维数组内部元素 拉平,变成一维数组,返回新数组,对原数组没有影响。

[1, 2, [3, 4]].flat(); // [1, 2, 3, 4]
复制代码

flat 默认只能 拉平 一层,若该数组为多维数组,可以提供入参 层数

[1, 2, [3, [4, 5]]].flat(2); // [1, 2, 3, 4, 5]
[1, [2, [3]]].flat(Infinity); // [1, 2, 3]
复制代码
  • flatMap()

对原数组的每个成员执行一个函数(相当于执行Array.prototype.map()),然后对返回值组成的数组执行 flat() 方法。该方法返回一个新数组,不改变原数组。需要注意的是返回值执行 flat() 时,只展开一层。

5.3、Object.fromEntries

Object.entries() 方法的作用是返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环也枚举原型链中的属性)。Object.fromEntries() 函数传入一个键值对的列表,并返回一个带有这些键值对的新对象。这个迭代参数应该是一个能够实现@iterator 方法的的对象,返回一个迭代器对象。它生成一个具有两个元素的类似数组的对象。

第一个元素 -> 用作属性键的值
第二个元素 -> 与该属性键关联的值

const arr = [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ];
const obj = Object.fromEntries(arr);
console.log(obj); // { 0: "a", 1: "b", 2: "c" }
复制代码

5.4、针对之前的新特性做了些调整和修改

  • Symbol.prototype.description 增加了 description 属性

  • Function.prototype.toString() 对函数实例的 toString() 方法做出了修改

  • 简化 try {} catch {} 修改 catch 绑定

  • String.prototype.matchAll

六、ES11(2020)

6.1、可选链操作符

const obj = {first: {second: 222}};
let nest = obj && obj.first && obj.first.second;
let nest = obj?.first?.second; // 222
let nest = obj?.first?.third; // undefined
复制代码

6.2、空位合并操作符

let c = a ? a : b;
let c = a || b;
复制代码

6.4、Null 判断运算符

判断某个属性值是否为 nullundefined 时,通常会使用 ||

let values = {
    name: null,
    age: undefined,
    key: 1,
    str: '',
    isExist: false,
    index: 0,
};
values.name || 'Hello, world!';
复制代码

但实际应用过程中我们发现,当属性值为 ''false0 中任意一个均生效,显然不能完全满足我们的意愿。于是引入了一个新的 Null 判断运算符 ??

values.str ?? 'Hello, world!';
复制代码

需要注意的是,与 &&|| 运算符同时使用时,由于优先级的问题,需要使用圆括号进行包裹,否则会报异常。

6.4、BigInt(大整数)

JavaScript 所有数字都保存成 64 位浮点数,这给数值的表示带来了两大限制。一是数值的精度只能到 53 个二进制位(相当于 16 个十进制位),大于这个范围的整数,JavaScript 是无法精确表示的,这使得 JavaScript 不适合进行科学和金融方面的精确计算。二是大于或等于 2 的 1024 次方的数值,JavaScript 无法表示,会返回 Infinity。这里引入新的数据类型 BigInt,只用来表示整数,且没有位数的限制。

1234 // 普通整数
1234n // BigInt

1n + 2n // 3n
复制代码

以前计算

let p = 1;
for (let i = 1; i <= 70; i++) {
    p *= i;
}
console.log(p); // 1.197857166996989e+100
复制代码

BigInt

let p = 1n;
for (let i = 1n; i <= 70n; i++) {
    p *= i;
}
console.log(p);
// `11978571669969891796072783721689098736458938142546425857555362864628009582789845319680000000000000000n
复制代码

6.5、动态引入

现在前端打包资源越来越大,前端应用初始化时根本不需要全部加载这些逻辑资源,为了首屏渲染速度更快,很多时候都是动态导入(按需加载)模块,比如懒加载图片等,这样可以帮助您提高应用程序的性能。

el.onclick = () => {
    import('/modules/my-module.js')
        .then(module => {
        // Do something with the module.
        })
        .catch(err => {
        // load error;
        })
}
复制代码

6.6、私有字段

class Flower {
    #leafColor = 'green';

    constructor(name) {
        this.name = name;
    }

    getColor() {
        return this.#leafColor;
    }
}

const orchid = new Flower('orchid');

console.log(orchid.getColor());
console.log(orchid.#leafColor);
复制代码

6.7、globalThis

globalThis 是一个全新的标准方法用来获取全局 this

globalThis.Array(0,1,2); // [0,1,2]
globalThis.v = { value: true };
复制代码

七、ES12(2021)

7.1、replaceAll()

replace() 只能替换符合条件的第一个匹配,若想替换所有的匹配这需要使用正则表达式中的 g。然而 replaceAll() 可以替换字符串中的所有匹配。

'aabbcc'.replace(/b/g, '_'); // "aa__cc"
'aabbcc'.replaceAll('b', '_'); // "aa__cc"
复制代码

7.2、Promise.any

接收多个 Promise 实例参数,只要有一个实例成功就会返回。Promise.any()Promise.race() 方法很像,只有一点不同,就是不会因为某个 Promise 变成 rejected 状态而结束。

async function anyPromise() {
    const promises = [
        fetch('http://yapi.devops.guchele.com/mock/288/rpm-web/category/child/').then(() => 'a'),
        fetch('http://yapi.devops.guchele.com/mock/288/rpm-web/category/child/').then(() => 'b'),
        fetch('http://yapi.devops.guchele.com/mock/288/rpm-web/category/child/').then(() => 'c'),
        // fetch('/rpm-web/category/child/').then(() => 'a'),
        // fetch('/rpm-web/category/child/').then(() => 'b'),
        // fetch('/rpm-web/category/child/').then(() => 'c'),
    ];

    await Promise.any(promises)
        .then(res => {
            console.log('Promise.any:', res);
        })
        .catch(error => {
            console.log('Promise.any异常:', error);
        });
}

anyPromise();
复制代码

其中只要有一个变成fulfilledPromise.any()返回的 Promise 对象就变成fulfilled。如果所有三个操作都变成rejected,那么await命令就会抛出错误。

Promise.any异常: AggregateError: All promises were rejected
复制代码

7.3、数值分隔符

日常生活中,我们可能会发现有些较长的数值允许每三位添加一个分隔符(通常是逗号),比如,1000 可以写作 1,000,增加数值的可读性。
ECMAScript 中退出 _,允许 JavaScript 的数值使用下划线(_)作为分隔符,但是并没有指定数值分隔符间隔的位数,允许每三位添加一个分隔符,也允许每一位、每两位、每四位等添加一个,该数值包括整数、小数。

123_00 === 12_300 // true

12345_00 === 123_4500 // true
12345_00 === 1_234_500 // true
复制代码
  • 注意事项

不能在数值的最前面(leading)或最后面(trailing)
不能两个或两个以上的分隔符连在一起
小数点的前后不能有分隔符
科学计数法里面,表示指数的 eE 前后不能有分隔符

还有更多的新特性等待优秀的你们去发掘总结并实践

参考网址

网上有很多人会总结类似的 ECMAScript 的新特性,很多特性标记的年份或者正式发布的时间都是有差异的,后续有需要总结或者归纳的建议到官网中进行查找,推荐一下网站


南京三百云信息科技有限公司(车300)成立于2014年3月27日,是一家扎根于南京的移动互联网企业,目前坐落于南京、北京。经过7年积累,累计估值次数已达52亿次,获得了国内外多家优质投资机构青睐如红杉资本、上汽产业基金等。
三百云是国内优秀的以人工智能为依托、以汽车交易定价和汽车金融风控的标准化为核心产品的独立第三方的汽车交易与金融SaaS服务提供商。

欢迎加入三百云,一起见证汽车行业蓬勃发展,期待与您携手同行!
Java开发、Java实习、PHP实习、测试、测开、产品经理、大数据、算法实习,热招中…
官网:www.sanbaiyun.com/
投递简历:hr@che300.com,请注明来自掘金?

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享