State Of Js 2022 中的 ES 语言特性 报告地址:https://2022.stateofjs.com/en-US/
mdn文档说明
用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
Proxy 现在还是挺常用的,比如 Vue3、和 Reflect 配合使用元编程
1 2 3 4 5 6 7 8 9 10 const handler = { get : function (target, name) { return name in target ? target[name] : 42 ; }, }; const p = new Proxy ({}, handler);p.a = 1 ; console .log(p.a, p.b);
趋势
趋势有点奇怪,2021 年及之前了解人数/使用人数比例还在逐年上升,2022 年居然有所减少。
mdn文档说明
Promise.allSettled() 方法以 promise 组成的可迭代对象作为输入,并且返回一个 Promise 实例。当输入的所有 promise 都已敲定时(包括传递空的可迭代类型),返回的 promise 将兑现,并带有描述每个 promsie 结果的对象数组。
看起来感觉跟Promise.all()很像,但Promise.allSettled() 的最大不同点在于Promise.allSettled() 永远不会被 reject。
在使用 Promise.all()时,如果有一个 promise 出现了异常,被 reject 了,就不会走到.then,如:
1 2 3 4 5 6 7 8 9 const promises = [Promise .resolve(1 ), Promise .resolve(2 ), Promise .reject(3 )];Promise .all(promises).then(values => console .log(values));Promise .all(promises) .then(values => console .log(values)) .catch(err => console .log(err));
这种情况下,Promise.all()的关键问题在于:尽管能用 catch 捕获其中的异常,但你会发现其他执行成功的 promise 的消息都丢失了。
而Promise.allSettled不一样:
1 2 3 4 5 6 7 8 9 const promises = [Promise .resolve(1 ), Promise .resolve(2 ), Promise .reject(3 )];Promise .allSettled(promises).then(values => console .log(values));
可以看到所有 promise 的数据都被包含在 then 语句中,且每个 promise 的返回值多了一个 status 字段
兼容性
polyfill:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 if (Promise && !Promise .allSettled) { Promise .allSettled = function (promises ) { return Promise .all( promises.map(function (promise ) { return promise .then(function (value ) { return { state : 'fulfilled' , value : value }; }) .catch(function (reason ) { return { state : 'rejected' , reason : reason }; }); }) ); }; }
趋势
总体来看这几年使用有所上升
mdn文档说明
关键字 import 可以像调用函数一样来动态的导入模块。以这种方式调用,将返回一个 promise。
1 2 3 import ('/modules/my-module.js' ).then(module => { });
这种使用方式也支持 await 关键字。
1 2 3 const module = await import ('/modules/my-module.js' );
兼容性
趋势
mdn文档说明
类属性在默认情况下是公有的,但可以使用增加哈希前缀 # 的方法来定义私有类字段,这一隐秘封装的类特性由 js 自身强制执行。如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class ClassWithPrivateField { #privateField; } class ClassWithPrivateMethod { #privateMethod() { return 'hello world' ; } } class ClassWithPrivateStaticField { static #PRIVATE_STATIC_FIELD; } class ClassWithPrivateStaticMethod { static #privateStaticMethod() { return 'hello world' ; } }
从作用域之外引用 # 名称、内部在未声明的情况下引用私有字段、或尝试使用 delete 移除声明的字段都会抛出语法错误。如:
1 2 3 4 5 6 7 8 9 10 11 12 13 class ClassWithPrivateField { #privateField; constructor () { this.#privateField = 42; delete this.#privateField; // 语法错误 this.#undeclaredField = 444; // 语法错误 } } const instance = new ClassWithPrivateField()instance.#privateField === 42; // 语法错误
并且类似于公有字段,私有字段在构造(construction)基类或调用子类的 super() 方法时被添加到类实例中。
私有实例方法 1 2 3 4 5 6 7 8 9 10 11 12 13 class ClassWithPrivateMethod { #privateMethod() { return 'hello world' ; } getPrivateMessage() { return this.#privateMethod(); } } const instance = new ClassWithPrivateMethod();console .log(instance.getPrivateMessage());
兼容性 私有性还是比较好做 polyfill 的,比如:
1 2 3 4 5 6 7 8 9 10 11 class Test { #num = 1; set setNum(num) { this.#num = num; } get getNum() { return this.#num; } }
babel:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 function _classPrivateFieldInitSpec (obj, privateMap, value ) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); } function _checkPrivateRedeclaration (obj, privateCollection ) { if (privateCollection.has(obj)) { throw new TypeError ('Cannot initialize the same private elements twice on an object' ); } } function _classPrivateFieldGet (receiver, privateMap ) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, 'get' ); return _classApplyDescriptorGet(receiver, descriptor); } function _classApplyDescriptorGet (receiver, descriptor ) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; } function _classPrivateFieldSet (receiver, privateMap, value ) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, 'set' ); _classApplyDescriptorSet(receiver, descriptor, value); return value; } function _classExtractFieldDescriptor (receiver, privateMap, action ) { if (!privateMap.has(receiver)) { throw new TypeError ('attempted to ' + action + ' private field on non-instance' ); } return privateMap.get(receiver); } function _classApplyDescriptorSet (receiver, descriptor, value ) { if (descriptor.set) { descriptor.set.call(receiver, value); } else { if (!descriptor.writable) { throw new TypeError ('attempted to set read only private field' ); } descriptor.value = value; } } var _num = new WeakMap ();class Test { constructor () { _classPrivateFieldInitSpec(this , _num, { writable: true , value: 1 , }); } set setNum(num) { _classPrivateFieldSet(this , _num, num); } get getNum() { return _classPrivateFieldGet(this , _num); } }
趋势
逐渐上升。
mdn文档说明
空值合并运算符(??)是一个逻辑运算符,当左侧的操作数为 null 或者 undefined 时,返回其右侧操作数,否则返回左侧操作数。
与逻辑或运算符(||)不同,逻辑或运算符会在左侧操作数为假值时返回右侧操作数。也就是说,如果使用 || 来为某些变量设置默认值,可能会遇到意料之外的行为。比如为假值(例如,'' 或 0)时。见下面的例子。
1 2 3 4 5 6 7 const foo = null ?? 'default string' ;console .log(foo);const baz = 0 ?? 42 ;console .log(baz);
兼容性
兼容处理也很简单,polyfill 如:
1 2 3 function getNum (num ) { return num ?? 1 ; }
babel:
1 2 3 function getNum (num ) { return num !== null && num !== void 0 ? num : 1 ; }
趋势
还算是在上升。
mdn文档说明
增强数字可读性的分隔符_。
1 2 3 4 5 6 1 _000_000_000_000;1 _050.95 ;0b1010 _0001_1000_0101;0o2 _2_5_6;0xa0 _b0_c0;1 _000_000_000_000_000_000_000n;
兼容性
babel 处理时去掉分隔符就好了。
趋势
有所上升。
mdn文档说明
1 2 3 4 5 6 7 8 9 const p = 'The quick brown fox jumps over the lazy dog. If the dog reacted, was it really lazy?' ;console .log(p.replaceAll('dog' , 'monkey' ));const regex = /Dog/gi ;console .log(p.replaceAll(regex, 'ferret' ));
兼容性
Corejs 的 polyfill 可见https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.replace-all.js
趋势
有所上升。
mdn文档说明
1 2 3 4 5 6 7 8 9 10 const regexp = /t(e)(st(\d?))/g ;const str = 'test1test2' ;const array = [...str.matchAll(regexp)];console .log(array[0 ]);console .log(array[1 ]);
兼容性
Corejs 的 polyfill 可见https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.match-all.js
趋势
有所上升。
mdn文档说明
Logical OR assignment: (x ||= y)
Logical AND assignment: (x &&= y)
如:
1 2 3 4 5 6 7 8 9 10 let a = 1 ;let b = 0 ;a &&= 2 ; console .log(a);b &&= 2 ; console .log(b);
1 2 3 4 5 6 7 let x = 0 ;let y = 1 ;x &&= 0 ; x &&= 1 ; y &&= 1 ; y &&= 0 ;
兼容性
趋势
有所上升。
mdn文档说明
Promise.any() 接收一个由 Promise 所组成的可迭代对象,该方法会返回一个新的 promise,一旦可迭代对象内的任意一个 promise 变成了兑现状态,那么由该方法所返回的 promise 就会变成兑现状态,并且它的兑现值就是可迭代对象内的首先兑现的 promise 的兑现值。如果可迭代对象内的 promise 最终都没有兑现(即所有 promise 都被拒绝了),那么该方法所返回的 promise 就会变成拒绝状态,并且它的拒因会是一个 AggregateError 实例,这是 Error 的子类,用于把单一的错误集合在一起。
1 2 3 4 5 6 7 8 9 const promise1 = Promise .reject(0 );const promise2 = new Promise (resolve => setTimeout(resolve, 100 , 'quick' ));const promise3 = new Promise (resolve => setTimeout(resolve, 500 , 'slow' ));const promises = [promise1, promise2, promise3];Promise .any(promises).then(value => console .log(value));
返回值
如果传入了一个空的可迭代对象,那么就会返回一个已经被拒的 promise
如果传入了一个不含有 promise 的可迭代对象,那么就会返回一个异步兑现的 promise
其余情况下都会返回一个处于等待状态的 promise。如果可迭代对象中的任意一个 promise 兑现了,那么这个处于等待状态的 promise 就会异步地(调用栈为空时)切换至兑现状态。如果可迭代对象中的所有 promise 都被拒绝了,那么这个处于等待状态的 promise 就会异步地切换至被拒状态。
兼容性
Corejs 的 polyfill 可见https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.promise.any.js
趋势
居然有所降低。
mdn文档说明
at() 方法接收一个整数值并返回该索引对应的元素,允许正数和负数。负整数从数组中的最后一个元素开始倒数。
1 2 3 4 5 6 7 8 9 10 11 const array1 = [5 , 12 , 8 , 130 , 44 ];let index = 2 ;console .log(`Using an index of ${index} the item returned is ${array1.at(index)} ` );index = -2 ; console .log(`Using an index of ${index} item returned is ${array1.at(index)} ` );
参数: index, {String}
要返回的数组元素的索引(位置)。当传递负数时,支持从数组末端开始的相对索引;也就是说,如果使用负数,返回的元素将从数组的末端开始倒数。
返回值
匹配给定索引的数组中的元素。如果找不到指定的索引,则返回 undefined。
兼容性
Corejs 的 polyfill 可见https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.array.at.js
趋势
呈上升趋势。
mdn文档说明
在模块的顶层,你可以单独使用关键字 await(异步函数的外面)。也就是说一个模块如果包含用了 await 的子模块,该模块就会等待该子模块,这一过程并不会阻塞其它子模块。
下面是一个在 export 表达式中使用了 Fetch API 的例子。任何文件只要导入这个模块,后面的代码就会等待,直到 fetch 完成。
1 2 3 4 const colors = fetch('../data/colors.json' ).then(response => response.json());export default await colors;
兼容性
趋势
呈上升趋势。
mdn文档说明
一个新的日期/时间 API,具体使用https://tc39.es/proposal-temporal/docs/index.html
1 console .log('Initialization complete' , Temporal.Now.instant());
兼容性
不兼容
趋势
新 APi,也没呈现出趋势
mdn文档说明
findLast() 方法返回数组中满足提供的测试函数条件的最后一个元素的值。如果没有找到对应元素,则返回 undefined。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 const inventory = [ { name : 'apples' , quantity : 2 }, { name : 'bananas' , quantity : 0 }, { name : 'fish' , quantity : 1 }, { name : 'cherries' , quantity : 5 }, ]; function isNotEnough (item ) { return item.quantity < 2 ; } console .log(inventory.findLast(isNotEnough));
兼容性
Corejs 的 polyfill 可见https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.array.find-last.js
趋势
mdn文档说明
导致一个错误的数据属性实例表明错误的具体的原始致因。 使用它当捕获和抛出收到一个错误更具体的或有用的错误信息仍然为了获得最初的错误。
1 2 3 4 5 try { connectToDatabase(); } catch (err) { throw new Error ('Connecting to database failed.' , { cause : err }); }
兼容性
趋势
mdn文档说明
Object.hasOwn() 用来代替 Object.prototype.hasOwnProperty().
1 2 3 4 5 6 7 8 9 10 11 12 const object1 = { prop: 'exists' , }; console .log(Object .hasOwn(object1, 'prop' ));console .log(Object .hasOwn(object1, 'toString' ));console .log(Object .hasOwn(object1, 'undeclaredPropertyValue' ));
兼容性
Corejs 的 polyfill 可见https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.object.has-own.js
趋势
mdn文档说明
1 2 3 4 5 6 7 8 9 const regex1 = new RegExp ('foo' , 'd' );console .log(regex1.hasIndices);const regex2 = new RegExp ('bar' );console .log(regex2.hasIndices);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const str1 = 'foo bar foo' ;const regex1 = /foo/ dg;console .log(regex1.hasIndices); console .log(regex1.exec(str1).indices[0 ]); console .log(regex1.exec(str1).indices[0 ]); const str2 = 'foo bar foo' ;const regex2 = /foo/ ;console .log(regex2.hasIndices); console .log(regex2.exec(str2).indices);
兼容性
趋势 å
Author
My name is Micheal Wayne and this is my blog.
I am a front-end software engineer.
Contact: michealwayne@163.com