ES9 ES10 更新点梳理

接着上文我们再来看看 ES8、9 的新特性,小白慢慢成长中...

ES9

  1. 异步迭代器 for await...of

for await...of 语句会在异步或者同步可迭代对象上创建一个迭代循环,包括 StringArrayArray-like 对象(比如 arguments 或者 NodeList ),TypedArrayMapSet 和自定义的异步或者同步可迭代对象。其会调用自定义迭代钩子,并为每个不同属性的值执行语句。像 String 表达式一样,这个语句只能在 async function 内使用

  • 那我们先来复习一下迭代器和生成器吧 ~

迭代器 是一个对象,它定义一个序列,并在终止时可能返回一个返回值。 更具体地说,迭代器是通过使用 next() 方法实现 terator protocol 的任何一个对象,该方法返回具有两个属性的对象: value ,这是序列中的 next 值;和 done ,如果已经迭代到序列中的最后一个值,则它为 true 。如果 valuedone 一起存在,则它是迭代器的返回值

首先我们知道 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 {<fulfilled>: {…}}
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();
  1. 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

  1. 正则方法的增强
  • 正则表达式命名捕获组

    这个功能对于我们匹配一些字符串还是比较好使的,例如

// 需求: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 = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[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, `$<month>-$<day>-$<year>`);
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

  1. 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]]
  1. String 增加了 trimStart 和 trimEnd 方法

就是去除首位的空白字符和末尾的空白字符

  1. 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"}

  1. 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 引擎的代码已经升级修复了这个问题
  1. 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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351