接着上文我们再来看看 ES8、9 的新特性,小白慢慢成长中...
ES9
- 异步迭代器 for await...of
for await...of 语句会在异步或者同步可迭代对象上创建一个迭代循环,包括 String
,Array
,Array-like
对象(比如 arguments
或者 NodeList
),TypedArray
,Map
, Set
和自定义的异步或者同步可迭代对象。其会调用自定义迭代钩子,并为每个不同属性的值执行语句。像 String
表达式一样,这个语句只能在 async
function
内使用
- 那我们先来复习一下迭代器和生成器吧 ~
迭代器 是一个对象,它定义一个序列,并在终止时可能返回一个返回值。 更具体地说,迭代器是通过使用 next()
方法实现 terator protocol
的任何一个对象,该方法返回具有两个属性的对象: value
,这是序列中的 next
值;和 done
,如果已经迭代到序列中的最后一个值,则它为 true
。如果 value
和 done
一起存在,则它是迭代器的返回值
首先我们知道 Array
String
Map
Set
TypedArray
都是内置迭代器的
// 验证一下
const arr = [1,2], str = '123'
console.log(arr[Symbol.iterator]) // ƒ values() { [native code] }
console.log(str[Symbol.iterator]) // ƒ [Symbol.iterator]() { [native code] }
但是我们常用的 Object
是没有内置迭代器的
// 验证一下
const obj = { name: 'tal', age: 17 }
console.log(obj[Symbol.iterator]) // undefined
那如果我们需要使用的时候呢,可能就需要自己实现一个了
const obj = { name: 'tal', age: 17 }
// 这行代码会报错,所以我们自己实现一个
// VM123:3 Uncaught TypeError: obj is not iterable
for (const val of obj) {
console.log('没有自定义迭代器', val);
}
obj[Symbol.iterator] = function() {
const me = this;
const keys = Object.keys(me);
const len = keys.length;
let pointer = 0;
return {
next() {
const done = pointer >= len;
const value = !done ? me[keys[pointer++]] : undefined;
return {
value,
done
};
}
};
};
console.log(obj[Symbol.iterator]);
for (const val of obj) {
console.log('自定义迭代器' ,val);
}
生成器 函数提供了一个强大的选择:它允许你定义一个包含自有迭代算法的函数, 同时它可以自动维护自己的状态。 生成器函数使用 function*
语法编写。 最初调用时,生成器函数不执行任何代码,而是返回一种称为 Generator
的迭代器。 通过调用生成器的下一个方法消耗值时,Generator
函数将执行,直到遇到 yield
关键字
// 执行函数时,并不会执行函数体
function* fn() {
console.log("正常函数我会执行");
yield 1;
yield 2;
yield 3;
console.log("执行完了");
}
const iteratorFn = fn(); //只是创建了一个iterator
console.log(iteratorFn.next()) // 正常函数我会执行 {value: 1, done: false};
console.log(iteratorFn.next()); // {value: 2, done: false}
console.log(iteratorFn.next()); // {value: 3, done: false} 执行完了
console.log(iteratorFn.next()); // {value: undefined, done: true}
再来介绍一下同步迭代器和异步迭代器的区别吧
同步迭代器: next() => { value:'', done: false }
异步迭代器: next() => Promise
// 我们先来手写一个异步迭代器
const createAsyncIterator = items => {
const keys = Object.keys(items);
const len = keys.length;
let pointer = 0;
return {
next() {
const done = pointer >= len;
const value = !done ? items[keys[pointer++]] : undefined;
return Promise.resolve({
value,
done
});
}
};
};
const asyncI = createAsyncIterator([1, 2, 3]);
console.log(asyncI.next()) // Promise {: {…}}
asyncI.next().then(res => {
console.log(res); // {value: 1, done: false}
});
asyncI.next().then(res => {
console.log(res); // {value: 2, done: false}
});
asyncI.next().then(res => {
console.log(res); // {value: 3, done: false}
});
asyncI.next().then(res => {
console.log(res); // {value: undefined, done: true}
});
与迭代器相同的是,Object
也没有内置的迭代器
对应生成器,也有异步生成器
async function* fn() {
yield await Promise.resolve(1);
yield await Promise.resolve(2);
yield await Promise.resolve(3);
}
const asyncI = fn();
// 使用一下 for await ...of
async function fn1() {
for await (const val of asyncI) {
console.log(val);
}
}
fn1();
- Rest/Spread 属性
在 ES6 的时候我们就已经开始使用解构来简化我们的代码,但仅仅是数组,在 ES 9 的时候,他被增强了,
直接上代码
// 作为参数
function fn(a, b, ...c) {
console.log(a, b, c);
}
fn(1, 2, 3, 4, 5);
// 对象的展开
const obj = {
name: "tal",
age: 17,
info: {
phone: 188888
}
};
const { name, ...infos } = obj;
console.log(name, JSON.stringify(infos));
// tal {"age":17,"info":{"phone":188888}}
function fn({ name, ...infos }) {
console.log(name, infos);
}
fn(obj);
// 对象的合并
const obj2 = { ...obj, address: "beijing" };
console.log(obj2);
// {name: "tal", age: 17, info: {phone: 188888}, address: "beijing"}
// 还可以用来做对象浅拷贝
const objClone = { ...obj };
objClone.name = "www";
objClone.info.phone = 10;
console.log(objClone.info.phone);
console.log(obj.info.phone);
可以使用扩展运算符拷贝一个对象,像是这样 objClone = {...obj },但是 这只是一个对象的浅拷贝。另外,如果一个对象A的属性是对象B,那么在克隆后的对象cloneB中,该属性指向对象B
- 正则方法的增强
-
正则表达式命名捕获组
这个功能对于我们匹配一些字符串还是比较好使的,例如
// 需求:YYYY-MM-DD 年/月日解析到数组中
// 可能我们会这样做
const dateStr = "2030-08-01";
const reg = /([0-9]{4})-([0-9]{2})-([0-9]{2})/;
const res = reg.exec(dateStr);
console.log(res[1], res[2], res[3]);
// 但是有了捕获组,我们可以这样去操作
const reg1 = /(?[0-9]{4})-(?[0-9]{2})-(?[0-9]{2})/;
const res1 = reg1.exec(dateStr);
console.log(res1.groups.year, res1.groups.month, res1.groups.day)
// 在 replace 中也可以使用
将年份放到最后一位
const newDate = dateStr.replace(reg1, `$-$-$`);
console.log(newDate);
- 反向断言、先行断言
用这个功能还是比较好匹配一些符号或者获取其中的内容,大佬们感兴趣可以下去试试
// 获取货币符号
const str = "$111122223333"
// 先行断言 (?=pattern)
const reg = /\D(?=\d+)/;
const result = reg.exec(str);
console.log(result[0]); // $
// 后行断言 反向断言 (?<=pattren)
const reg1 = /(?<=\D)\d+/;
console.log(reg1.exec(str)[0]); // 111122223333
正则还有 dotAll
Unicode转义
非转义序列的模板字符串
这几个功能的更新,我就不一一介绍了,确实对于正则我这方面有些薄弱,比较头大...
ES10
- Array.prototype.flat() 和 Array.prototype.flatMap() 方法
flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
// 可能最常用的就是数组的扁平化了
const arr = [1, 2, 3, [4, 5]];
const arr1 = [1, 2, 3, [4, 5, [6, 7, [8, 9, [10, 11]]]]];
// 指定遍历深度
// console.log(arr1.flat(3));
// 指定任意深度
console.log(arr1.flat(Infinity));
// 去除数组的空项
const arr2 = [1, 2, , , , , 3];
console.log(arr2.flat()); // [1,2,3]
flatMap() 方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。它与 map
连着深度值为1的 flat
几乎相同,但 flatMap
通常在合并成一种方法的效率稍微高一些。
const arr1 = [0, 5, 2, 3];
console.log(arr1.map(x => [x * 2])); // [[0], [10], [4], [6]]
console.log(arr1.flatMap(x => [x * 2])); // [0, 10, 4, 6]
// 只会将 flatMap 中的函数返回的数组 “压平” 一层
console.log(arr1.flatMap(x => [[x * 2]])); // [[0], [10], [4], [6]]
- String 增加了 trimStart 和 trimEnd 方法
就是去除首位的空白字符和末尾的空白字符
- Object.formEntries
Object.entries() 方法的作用是返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环也枚举原型链中的属性)
而 Object.fromEntries()
则是 Object.entries()
的反转。
Object.fromEntries()
函数传入一个键值对的列表,并返回一个带有这些键值对的新对象。这个迭代参数应该是一个能够实现 @iterator
方法的的对象,返回一个迭代器对象。它生成一个具有两个元素的类似数组的对象,第一个元素是将用作属性键的值,第二个元素是与该属性键关联的值。
const map = new Map([
["name", "tal"],
["age", "17"]
]);
console.log(Object.fromEntries(map));
// {name: "tal", age: "17"}
- Array.prototype.sort()
这个可能我们平常开发中就用的比较多了,我就只介绍一些有趣的点
// 思考一个小问题
console.log([1,11,23,2,3].sort()) // 这个会输出什么
有没有大佬去研究一些 sort
是用什么方法排序的?
我专门去查询了一下网上的一些博客,发现他们写的,在数组长度小于10的时候,用的是插入排序,在大于10的时候,使用的是快速排序
但是我们现在使用的肯定不是他们说的这样的,因为快速排序是一种非稳定性排序,那什么是非稳定排序呢?给大家举个例子吧
const arr = [
{ name : 'a' , age: 17},
{ name : 'b' , age: 2},
{ name : 'c' , age: 2},
]
console.log(arr.sort((a, b) => b.age - a.age ))
// 如果是非稳定性排序的话,排序完成后的数组,第一位应该是 c ,但是现在我们执行这段代码的话,第一位是 b
,这就说明 V8 引擎的代码已经升级修复了这个问题
- BigInt() 类型 任意精度整数
BigInt 是一种数字类型的数据,它可以表示任意精度格式的整数。而在其他编程语言中,可以存在不同的数字类型,例如:整数、浮点数、双精度数或大斐波数。
const maxNum = Number.MAX_SAFE_INTEGER
let num = 1n;
let num2 = 10n;
const bigIntNum = BigInt(maxNum);
console.log(bigIntNum);
console.log(maxNum + 2) // 不能展示正确的答案
console.log(bigIntNum + 2n) // 正确
console.log("类型", typeof num); // bigInt
console.log("双等下的比较", num == 1); // true
console.log("三等下的比较", num === 1); // false