接着上一篇快速了解ES特性之二(ES9) ,今天我们主要来讲ES新特性之 ES10 。前面我们已经讲了 ES 的由来、版本对应规则等等内容,这里我就不再赘述,我们直接开始
快速了解ES特性 是我的系列文章,现在已有
ES2019 / ES10
Optional catch binding 可选的异常捕获
在 ES10 之前我们在 try catch
的时候,必须在 catch
的时候指定一个抛出的错误。现在则不需要了。
下面简单举一下例子
// ES10 之前
try {
throw `error`;
} catch (e) { // 必须申明捕获的异常
console.log(e);
}
// 现在
try {
throw `error`;
} catch { // 这里捕获的错误现在是可选的了,可以不写
}
JSON superset
在 ES10 之前,ECMAScript 并不支持所有的 JSON格式 。正常的 JSON字符 可以包含未转义的 U+2028 LINE SEPARATOR
和 U+2029 PARAGRAPH SEPARATOR
字符,但 ECMAScript 却是不支持的。现在从 ES10
开始就可以愉快的支持啦。🤣
const code = '"\u2028\u2029"';
// 以前下面两种写法会炸,报'SyntaxError',现在不会了
JSON.parse(code);
eval(code);
Symbol.prototype.description
在 ES10 之前,我们知道 Symbol
的描述只是通过构造传入,并不能直接获取到。如果我们要获取,只能通过 toString
转换
interface SymbolConstructor {
/**
* Returns a new unique Symbol value.
* @param description Description of the new Symbol object.
*/
(description?: string | number): symbol;
}
const hi = Symbol("hi");
console.log(hi.toString());
// Symbol(hi)
console.log(hi.toString() === "Symbol(hi)");
// true
现在我们可以直接获取到这个描述
interface Symbol {
/**
* Expose the [[Description]] internal slot of a symbol directly.
*/
readonly description: string | undefined;
}
const hi = Symbol("hi");
console.log(hi.description);
// hi
console.log(hi.description === "hi");
// true
详情同学们可以在这儿看:Symbol description accessor
Function.prototype.toString revision
我们直接看示例:
function hi() {
// hello world
console.log(`hello world`);
}
console.log(hi.toString());
// function hi() {
// // hello world
// console.log(`hello world`);
// }
console.log(hi.toString.toString());
// function toString() { [native code] }
我们从上面的示例可以看出,方法的 toString()
会打印方法的真源码,源码咋写的,就返回啥。这里也没啥好讲的,想要了解更多的同学可以在这儿查看详情:Function.prototype.toString revision
Object.fromEntries
我们在 ES8 中新增了 Object.entries
方法,可以将对象转成 entry 数组。在 ES10 则增加了一个从 entry 数组生成对象的方法。
interface ObjectConstructor {
/**
* Returns an object created by key-value entries for properties and methods
* @param entries An iterable object that contains key-value entries for properties and methods.
*/
fromEntries<T = any>(entries: Iterable<readonly [PropertyKey, T]>): { [k: string]: T };
/**
* Returns an object created by key-value entries for properties and methods
* @param entries An iterable object that contains key-value entries for properties and methods.
*/
fromEntries(entries: Iterable<readonly any[]>): any;
}
我们通过看上面的方法定义可以知道,Object.fromEntries
接受一个可迭代的对象,然后生成一个新的对象。下面我给大家举例说明一下
const obj = { a: 1, b: 2, c: 3 };
const entries = Object.entries(obj);
// [ [ 'a', 1 ], [ 'b', 2 ], [ 'c', 3 ] ]
const newObj = Object.fromEntries(entries);
// { a: 1, b: 2, c: 3 }
我们也可以从一个简单的 字符串为key 的 Map 中生成
const map = new Map().set(`a`, 1).set(`b`, 2).set(`c`, 3);
const obj = Object.fromEntries(map);
console.log(obj);
// { a: 1, b: 2, c: 3 }
如果不是字符串的 key,将会自动转换成字符串的
const map2 = new Map().set(1, 1).set([1, 2, 3], 2).set(Symbol(`hi`), 3);
const obj2 = Object.fromEntries(map2);
console.log(obj2);
// { '1': 1, '1,2,3': 2, [Symbol(hi)]: 3 }
像 Map
一样的可迭代对象也可以进行转换,比如:URLSearchParams
const obj4 = Object.fromEntries(new URLSearchParams(`x=1&y=2`));
console.log(obj4);
// { x: '1', y: '2' }
我们也可以自己简单模拟一个可迭代对象
const mapLike = {
kv: {},
get(key) {
return this.kv[key];
},
set(key, value) {
this.kv[key] = value;
return this;
},
[Symbol.iterator]() {
let index = 0;
const entries = Object.entries(this.kv);
return {
next() {
const entry = entries[index++];
return {
value: entry,
done: !entry,
};
},
};
},
};
mapLike.set(`a`, 1).set(`b`, 2).set(`c`, 3);
const obj5 = Object.fromEntries(mapLike);
console.log(obj5);
// { a: 1, b: 2, c: 3 }
我们也可以对对象数组进行降维操作,转换成单纯的 key-value
对象
const arr = [
{ name: `张三`, age: 26 },
{ name: `林大俊`, age: 18 },
];
const obj3 = Object.fromEntries(arr.map(({ name, age }) => [name, age]));
console.log(obj3);
// { '张三': 26, '林大俊': 18 }
Object.fromEntries
和 Object.entries
本就是一对,中间进行转换可以实现花里胡哨的效果。
这个 Object.fromEntries
也讲的差不多了,下面我们继续学习新的特性
Well-formed JSON.stringify
我们知道,当我们后端返回一个 JSON 数据的时候需要将 JSON 数据编码成 UTF-8 格式。但是当我们使用 JSON.stringify
来序列化的时候,不能将一些非成对的 UTF-16 的代码单元(0xD800–0xDFFF)编码成 UTF-8 。我们可以了解到 UTF-16 代理对是由一个高代理(U+D800—U+DBFF 1,024 个代码点)+ 一个低代理(U+DC00—U+DFFF 1,024 个代码点)组成。我们用屁股想一下也知道,只有一半的 UTF-16 肯定没法子正确的编码成一个对的字符。所以在 ES10 修改了一下 JSON.stringify
,让它可以以一种合理的方式处理这种 错误的字符 :在处理这部分字符的时候,用转义字符代替,而不是编码的方式。
// 正确成对的UTF-16编码组成的非BMP字符,能够正确打印
JSON.stringify('𝌆')
// '"𝌆"'
JSON.stringify('\uD834\uDF06')
// '"𝌆"'
// 错误未配对的UTF-16代理代码单元会被转换成转义字符
JSON.stringify('\uDF06\uD834')
// '"\udf06\ud834"'
// 单个UTF-16代理代码单元也会被转成转义字符
JSON.stringify('\uDEAD')
// '"\udead"'
下面的示例展示了 JSON.stringify
是以转义字符的方式处理这部分字符的
console.log(JSON.stringify(`\u{D800}`) === `"\u{D800}"`);
// false
console.log(JSON.stringify(`\u{D800}`) === `"\ud800"`);
// true
String.prototype.{trimStart,trimEnd}
这个感觉没有什么好讲的,主要是去除字符串两端的空白字符(空格、换行符等)。
interface String {
/** Removes the trailing white space and line terminator characters from a string. */
trimEnd(): string;
/** Removes the leading white space and line terminator characters from a string. */
trimStart(): string;
}
在 ES5 就实现了 trimLeft
和 trimRight
方法。但是呢,和 ES8
新增的 padStart
和 padEnd
名字有点格格不入(ps: start 和 end 的命名方式更适应 RTL ,更加友好),所以给 trimLeft
和 trimRight
取了个别名: trimStart
和 trimEnd
。为了保证兼容性,大多数引擎中会将 String.prototype.trimLeft.name
的值trimLeft
变为 trimStart
、 String.prototype.trimRight.name
的值trimRight
变为 trimEnd
。
下面我们举例说明一下
const str = ` hi, star \n`;
console.log(str.length);
// 11
console.log(str.trimStart().length);
// 10 去除了左边一个空格
console.log(str.trimEnd().length);
// 9 去除了右边一个空格和一个换行符
console.log(String.prototype.trimLeft.name === `trimStart`);
// true
console.log(String.prototype.trimRight.name === `trimEnd`);
// true
Array.prototype.{flat,flatMap}
ES10 又给数组新增两个操作符 flat
和 flatMap
。下面我将分开讲解一下它们的作用和用法
flat 扁平化数组
flat
的作用就如其名一般,将一个嵌套的数组 拍平,通过传入一个 depth
参数,可以将指定深度嵌套的数组解开 拍平,最终合并成一个新的数组返回。如果不传递 depth
参数,默认为 depth
为 1,将解开一层的嵌套数组。如果传入 Infinity
,则会 拍平 所有层级的嵌套数组,这里的所有,指的就是任意层级,随便怎么套,都会变成一维数组,这也是我们最常见的用法。
interface Array<T> {
flat<A, D extends number = 1>(
this: A,
depth?: D
): FlatArray<A, D>[]
}
下面请看示例
const arr = [1, [2, [3, [4, [5, 6]]]], [7, [8, [9, [10]]]]];
console.log(arr.flat());// 默认解开一层
// [ 1, 2, [ 3, [ 4, [ 5, 6 ] ] ], 7, [ 8, [ 9, [10] ] ] ]
console.log(arr.flat(2));
// [ 1, 2, 3, [ 4, [ 5, 6 ] ], 7, 8, [ 9, [ 10 ] ] ]
console.log(arr.flat(3));
// [ 1, 2, 3, 4, [ 5, 6 ], 7, 8, 9, [ 10 ] ]
console.log(arr.flat(Infinity));
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
flat
方法还有一个特性,在展开的同时会合并空项。这里的空项仅仅指的是没有值的项,而不是 null
或者 undefined
const arr2 = [1, , [2, , 3, [4, , 5]], null, undefined];
console.log(arr2.flat());
// [ 1, 2, 3, [ 4, , 5 ], null, undefined ] // 只会合并到指定层级的空项,更深层级的不会被合并
console.log(arr2.flat(Infinity));
// [ 1, 2, 3, 4, 5, null, undefined ]
说到这个就不得不说一下我看到的一个比较花哨的解法,同样是用来展开所有嵌套数组。但是却是用 Generator 方法
const arr = [1, [2, [3, [4, [5, 6]]]], [7, [8, [9, [10]]]]];
function* flat(arr) {
for (const it of arr) {
if (Array.isArray(it)) {
yield* flat(it);
} else {
yield it;
}
}
}
// 直接使用会返回一个挂起的 **Generator**
console.log(flat(arr));
// flat{<suspended>}
// 这里我们用展开符消费一下,这样我们就得到了完全展开的数组
const flatArr = [...flat(arr)];
console.log(flatArr);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
flatMap
flatMap
这个方法就是一个组合怪,它的作用就是将一个高纬度的值,转换成一个低纬度的值。它是 map
和 depth
为 1 的 flat
的组合。
interface Array<T> {
flatMap<U, This = undefined>(
callback: (this: This, value: T, index: number, array: T[]) => U | ReadonlyArray<U>,
thisArg?: This
): U[]
}
下面我们来看一下示例
const words = [`hello`, `world`];
const r1 = words.map((item) => item.split(``)).flat();
console.log(r1);
// ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd']
const r2 = words.flatMap((item) => item.split(``));
console.log(r2);
// ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd']
看起来是不是结果都一样?但是第二种方法的性能会高一些,毕竟第一种要跑两次方法,中间循环多次。如果后面遇到需要先 map
再 flat
一层的情况,我们就可以优先使用 flatMap
,毕竟性能更佳嘛。
flatMap
的使用我们也不能局限到上面的场景,看上面方法的定义,只要是返回一个数组就成。那么我们就可以实现对结果的新增、删除、修改
const words = [`hello`, `world`];
const r3 = words.flatMap((item, index) =>
(index === 0 ? item.split(``).concat(`,`) : item.split(``)));
console.log(r3);// 给单词的间隔加了一个逗号
// ['h', 'e', 'l', 'l', 'o', ',', 'w', 'o', 'r', 'l', 'd']
const nums = [1, 2, 3];
const r4 = nums.flatMap((item) => [item, item ** item]);
console.log(r4);
// [1, 1, 2, 4, 3, 27]
总结
时间她过的可真快啊,一眨眼又到了说晚安的时候了。在 ES10 新特性中我只感觉 JSON superset
和 Well-formed JSON.stringify
可能需要大家多多了解一下,这两个涉及到了 ES 根本的东西。其他的都是很常见的东西,在其他语言中可能N年前就有了。这些拓展都只是增加了体验,有无都无伤大雅。但是有总是香的,白嫖最爽,可以减少大家很多模板代码。所以,请务必多来点?
如果文章对您有帮助的话,欢迎 点赞
、 评论
、 关注
、 收藏
、 分享
,您的支持是我码字的动力,万分感谢!!!🌈
如果文章内容出现错误的地方,欢迎指正,交流,谢谢😘