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 {: {…}}
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 = /(?[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

  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

你可能感兴趣的:(ES9 ES10 更新点梳理)