2.8万字总结!!ES6到ES12常用新特性!

ES是什么?

ES是ECMAScript的缩写,也就是JavaScript的标准化规范。ECMAScript是一种由Ecma国际组织制定的脚本语言标准,它定义了JavaScript的语法、类型、操作符、对象和函数等基本组件。ES6(也称为ES2015)是ECMAScript的第六个版本,引入了许多新的语言特性和改进,如箭头函数、模板字面量、解构赋值等。随后的版本(如ES7、ES8等)也引入了许多新的功能和语法糖,以进一步改进和扩展JavaScript。

时间轴

timeline title ECMAScript 版本时间轴 ... 2009: ES5 (ECMAScript 5) 2015: ES6 (ECMAScript 2015) 2016: ES7 (ECMAScript 2016) 2017: ES8 (ECMAScript 2017) 2018: ES9 (ECMAScript 2018) 2019: ES10 (ECMAScript 2019) 2020: ES11 (ECMAScript 2020) 2021: ES12 (ECMAScript 2021) ...

各版本特性总结

版本 特性 描述
ES6 let 和 const 关键字 引入了块级作用域声明变量的关键字 let 和常量声明变量的关键字 const
ES6 函数参数默认值 允许在函数定义时为参数设置默认值
ES6 箭头函数 使用箭头 (=>) 定义函数,简化了函数的写法,并且自动绑定了当前作用域的 this
ES6 模板字符串 使用反引号 (`) 来定义字符串,可以在字符串中插入变量或表达式,并支持多行字符串
ES6 扩展操作符 使用扩展操作符 (...) 可以将数组或对象展开为单独的元素
ES6 解构赋值 可以从数组或对象中提取值并赋给变量
ES6 对象字面量简化 简化了对象的定义和属性的赋值方式
ES6 引入了类和继承的概念,使得 JavaScript 更像面向对象编程语言
ES6 模块化 支持使用 import 和 export 关键字进行模块化开发,可以方便地导入和导出模块
ES6 Promise 提供了一种更优雅的方式来处理异步操作,解决了回调地狱的问题
ES6 Symbol 引入了一种新的原始数据类型 Symbol,可以用来创建唯一的标识符
ES6 Map/WeakMap 和 Set/WeakSet 数据结构 提供了更灵活和高效的数据结构,Map 是一种键值对的集合,Set 是一种无重复值的集合,WeakMap 和 WeakSet 是弱引用版本,可以更好地处理内存和垃圾回收的问题
ES6 迭代器(Iterator)和 for...of 迭代器提供了一种遍历集合的方式,for...of 循环可以直接遍历可迭代对象
ES6 生成器(Generator) 可以生成多个值的函数,使用 function* 和 yield 关键字定义,可以暂停和恢复函数的执行
ES6 Proxy 提供了拦截和自定义操作的机制,可以代理对目标对象的访问和修改操作
ES6 Reflect 提供了一组静态方法来操作对象,比如获取对象的属性描述符、动态调用对象的方法等
ES6 数组对象扩展 引入了一些新的方法和属性,比如 Array.from()、Array.of()、Array.prototype.includes() 等,方便了数组的创建和操作
ES6 字符串对象扩展 引入了一些新的方法和属性,比如 String.prototype.startsWith()、String.prototype.endsWith()、String.prototype.includes() 等,方便了字符串的处理
ES6 Math 对象扩展 引入了一些新的方法和常量,比如 Math.trunc()、Math.sign()、Math.PI 等,提供了更多的数学计算功能
ES6 Object 对象扩展 引入了一些新的方法,比如 Object.assign()、Object.keys()、Object.values() 等,方便了对象的操作
ES6 正则对象扩展 引入了一些新的方法,比如 RegExp.prototype.flags、RegExp.prototype.sticky 等,增强了正则表达式的功能
ES7 Array.prototype.includes()方法 判断数组中是否包含指定的元素,返回布尔值
ES7 指数操作符 ** 计算指数幂的运算符
ES8 async/await 提供了更简洁和可读性更好的异步编程方式
ES8 Object.entries() 返回对象自身可枚举属性的键值对数组
ES8 Object.values() 返回对象自身可枚举属性的值组成的数组
ES8 Object.getOwnPropertyDescriptors() 返回指定对象所有自身属性的描述符
ES8 padStart()和padEnd() 在字符串的开头或结尾填充指定的字符,使字符串达到指定的长度
ES8 ShareArrayBuffer 一种新的共享内存对象,用于在多个线程之间共享数据,但因安全问题暂时在 Chrome、FireFox、Safari 中被禁用
ES9 for await...of 遍历异步可迭代对象的每个元素
ES9 Rest/Spread 属性 允许使用...语法来获取剩余的参数或将数组或对象展开为函数的参数
ES9 Promise.finally() 无论 Promise 对象的状态如何,都会执行的回调函数
ES9 正则表达式扩展 引入了一些新的功能,包括反向断言、命名捕获组、s 修饰符(dotAll 模式)、Unicode 属性转义等
ES10 数组扁平化方法 使用 flat() 方法将多维数组转换为一维数组
ES10 字符串去除开头和结尾的空格方法 使用 trimStart() 和 trimEnd() 方法去除字符串开头和结尾的空格
ES10 Object.fromEntries 将键值对数组转换为对象
ES10 Symbol.prototype.description 获取 Symbol 对象的描述
ES10 Function.prototype.toString() 返回函数的源代码字符串
ES10 catch 绑定 允许不使用参数绑定 catch 语句块中的错误对象
ES10 JSON.stringify() 增强 支持序列化 BigInt 类型的数据
ES11 globalThis 提供了一个标准的方式来获取全局对象,不再依赖于具体的环境
ES11 BigInt 引入了一种新的原始数据类型 BigInt,可以表示任意精度的整数
ES11 可选链操作符 允许在访问对象的属性时,如果属性不存在,不会报错,而是返回 undefined
ES11 空值合并操作符 允许在变量为 null 或 undefined 时,使用默认值
ES11 String.prototype.matchAll() 返回一个迭代器,包含了字符串中与正则表达式匹配的所有结果
ES11 import() 动态导入模块的方法
ES11 Promise.allSettled() 返回一个 Promise,等待所有 Promise 完成,并返回一个包含所有 Promise 结果的数组,不会抛出错误
ES12 数值分隔符 使用下划线 (_) 来分隔数值,提高数值的可读性
ES12 逻辑赋值运算符 引入了逻辑赋值运算符,简化了变量赋值的操作
ES12 String.prototype.replaceAll() 替换字符串中的所有匹配项
ES12 Promise.any() 返回一个 Promise,只要有一个 Promise 成功,就会返回该 Promise 的结果,不会等待其他 Promise 的完成

以上只列举了每个版本的一些主要特性,还有其他一些较小的更新和改进没有在表格中列出。

ES6

ES6是ECMAScript 2015的简称,是ECMAScript的第六个版本。它在2015年发布,也被称为ES2015。ES6引入了许多新的语法和功能,大大改进了JavaScript的编程体验和开发效率。一些常见的ES6特性包括箭头函数、类和模块的支持、模板字符串、解构赋值、默认参数值、扩展运算符、Promise等。ES6的新增特性使得JavaScript在语法和功能上更加现代化和强大,成为前端开发中使用最广泛的ECMAScript版本之一。

let和const关键字

  • let关键字用于声明一个块级作用域的变量。与以前的var关键字不同,let声明的变量只在其所在的块级作用域内有效,而不会被提升到函数作用域。这意味着在使用let声明的变量之前,必须先进行声明,否则会抛出ReferenceError错误。

  • const关键字用于声明一个常量,其值在声明后不能再改变。const声明的变量也是块级作用域的,与let类似,但其值是不可变的。

js 复制代码
if (true) {
  let x = 10; // 块级作用域变量
  const y = 20; // 块级作用域常量
  console.log(x); // 输出: 10
  console.log(y); // 输出: 20
  y = 10;//报错: Assignment to constant variable.
}

console.log(x); // 报错: x is not defined
console.log(y); // 报错: y is not defined

函数参数默认值

  • 在ES6中,我们可以在函数参数中设置默认值。这意味着如果调用函数时没有为参数提供值,它们将使用默认值。这对于简化函数调用和处理缺少参数的情况非常有用。
javascript 复制代码
function greet(name = "World") {
  console.log(`Hello, ${name}!`);
}

greet(); // 输出:Hello, World!
greet("hhh"); // 输出:Hello, hhh!

箭头函数

  • 箭头函数具有简洁的语法、清晰的上下文、继承外部作用域的arguments等特点,适合简化代码、避免this指向问题和明确的参数处理,但不适合用于构造函数和需要自己的this值的场景。
javascript 复制代码
// 箭头函数示例
const add = (a, b) => a + b;
console.log(add(2, 3)); // 输出: 5

// 函数表达式示例
const multiply = function(a, b) {
  return a * b;
};
console.log(multiply(2, 3)); // 输出: 6

模板字符串

  • 模板字符串是ES6中引入的一种新的字符串语法。它允许在字符串中插入变量或表达式,而不需要使用字符串拼接符号。模板字符串使用反引号``包围,并使用${}语法来插入变量或表达式。

  • ${}语法中,我们可以放置任何有效的JavaScript表达式,这些表达式的值将被插入到字符串中。

javascript 复制代码
const name = "world";
console.log(`hello ${name}`); // 输出: hello world

//插入dom标签
const parent = document.querySelector('.parent');
const content = '这是一个div标签'
const templateString = `<div>${content}</div>`;
parent.innerHTML = templateString;

扩展操作符

  • 扩展操作符用于展开可迭代对象(如数组、字符串等),将其元素逐个展开,以便于在函数调用、数组字面量、对象字面量等地方使用。

  • 在使用扩展操作符时,你需要在要展开的可迭代对象前面加上三个点(...)。

  1. 展开数组:
javascript 复制代码
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]
  1. 传递参数给函数:
javascript 复制代码
function sum(a, b, c) {
  return a + b + c;
}

const numbers = [1, 2, 3];

const result = sum(...numbers); // 6
  1. 浅拷贝数组或对象:
javascript 复制代码
const arr1 = [1, 2, 3];
const arr2 = [...arr1]; // [1, 2, 3]

const obj1 = {name: 'Alice', age: 20};
const obj2 = {...obj1}; // {name: 'Alice', age: 20}

解构赋值

  • ES6的解构赋值语法允许我们从数组或对象中提取值,并将它们赋给变量。这使得我们可以更简洁地进行变量赋值操作。

  • 解构赋值可以用于数组和对象。下面是一些示例:

  1. 数组解构赋值:
javascript 复制代码
let numbers = [1, 2, 3];
let [a, b, c] = numbers;
console.log(a); // 输出 1
console.log(b); // 输出 2
console.log(c); // 输出 3
  1. 对象解构赋值:
javascript 复制代码
let person = { name: 'John', age: 20 };
let { name, age } = person;
console.log(name); // 输出 'John'
console.log(age); // 输出 20

除了基本的数组和对象解构赋值外,ES6的解构赋值还提供了一些其他的操作。

  1. 剩余项(Rest)操作符:可以使用...语法来捕获剩余的项,并将它们赋给一个数组。这在处理变长参数或动态长度的数组时非常有用。例如:
javascript 复制代码
let numbers = [1, 2, 3, 4, 5];
let [a, b, ...rest] = numbers;
console.log(a); // 输出 1
console.log(b); // 输出 2
console.log(rest); // 输出 [3, 4, 5]
  1. 解构赋值还支持默认值,当解构的值为undefined时,会使用默认值:
javascript 复制代码
let person = { name: 'John' };
let { name, age = 18 } = person;
console.log(name); // 输出 'John'
console.log(age); // 输出 18
  1. 嵌套解构:可以在解构赋值中嵌套使用数组和对象的解构。例如:
javascript 复制代码
let numbers = [1, [2, 3], 4];
let [a, [b, c], d] = numbers;
console.log(a); // 输出 1
console.log(b); // 输出 2
console.log(c); // 输出 3
console.log(d); // 输出 4
  1. 对象属性别名:可以为解构赋值的变量设置别名,使用冒号来指定别名。例如:
javascript 复制代码
let person = { name: 'John', age: 20 };
let { name: fullName, age } = person;
console.log(fullName); // 输出 'John'
console.log(age); // 输出 20
  1. 解构赋值还可以在函数参数中使用,方便地提取函数参数中的值:
javascript 复制代码
function greet({ name, age }) {
  console.log(`Hello, ${name}! You are ${age} years old.`);
}

let person = { name: 'John', age: 20 };
greet(person); // 输出 'Hello, John! You are 20 years old.'

对象字面量简化

  • ES6引入了一种更简洁的方式来定义对象字面量,即ES6对象字面量语法。它提供了一种更方便的方法来定义和初始化对象属性。它是ES6中一个非常方便的特性,可以提高代码的可读性和可维护性。

  • 在ES6之前,我们通常使用以下方式定义对象字面量:

javascript 复制代码
var name = 'hhh';
var age = 18;

var person = {
  name: name,
  age: age
};
  • 在ES6中,我们可以使用更简洁的语法来定义对象字面量,还可以直接在对象字面量中定义方法,而不需要使用function关键字:
javascript 复制代码
const name = "hhh";
const age = 18;

const person = {
  name,
  age,
  fun() {
    console.log(this.name,this.age);
  },
};

  • 在ES5中,我们没有类的概念,而是通过构造函数和原型链来实现对象:
javascript 复制代码
function Book(title, author) {
  this.title = title;
  this.author = author;
}

Book.prototype.getSummary = function() {
  return "书名:" + this.title + ",作者:" + this.author;
};

// 创建Book类的实例
var book1 = new Book("三体", "刘慈欣");
console.log(book1.getSummary()); // 书名:三体,作者:刘慈欣
  • ES6引入了类(class)的概念,使得面向对象的编程变得更加直观和易于理解。类是一种蓝图或模板,用于创建具有相同属性和方法的对象。
javascript 复制代码
class Book {
  constructor(title, author) {
    this.title = title;
    this.author = author;
  }

  getSummary() {
    return `书名:${this.title} ,作者:${this.author} `;
  }
}

// 创建Book类的实例
const book1 = new Book("三体", "刘慈欣");
console.log(book1.getSummary()); //书名:三体 ,作者:刘慈欣 

模块化

  • 模块化是一种组织和管理JavaScript代码的方法,它将代码拆分为独立的模块,每个模块都有自己的作用域和功能。这种方法有助于提高代码的可维护性、可重用性和可扩展性。

  • 在ES6之前,JavaScript并没有原生的模块化支持。开发人员通常使用一些第三方库或模式来实现模块化,例如CommonJSAMD

  • 然而,ES6引入了原生的模块化系统,通过importexport关键字来实现。下面是一个示例:

javascript 复制代码
// utils.js
// 导出一个常量
export const PI = 3.14;
// 导出一个函数
export const area = (r) => PI * r ** 2

// main.js
// 导入常量和函数
import { PI, area } from './utils.js';
console.log(PI,area(3));//3.14 28.26

Promise

  • Promise是一个表示异步操作最终完成或失败的对象。

  • 它可以有三种状态:

    • pending(进行中)
    • resolved(已完成)
    • rejected(已失败)
  • 创建一个Promise对象:new Promise((resolve, reject) => {})

    • resolve :将状态从进行中变为完成,在异步操作成功时调用,并将异步操作的结果作为参数传递出去
    • reject :将状态从进行中变为失败,在异步操作失败时调用,并将异步操作的错误作为参数传递出去
  • 主要方法:

    • then():用于处理异步操作成功的情况

    • catch():用于处理异步操作失败的情况。

    • Promise.all() :接收一个每个元素都是一个Promise 对象的可迭代对象(如数组)作为参数,它会返回一个新的 Promise 对象,该 Promise 对象的状态取决于传入的所有 Promise 对象的状态(其中任何一个Promise对象状态为 rejected 状态,返回的 Promise 对象状态会立即变为 rejected),返回的 Promise 对象的结果是有序的,与传入的 Promise 对象的顺序相同。

    • Promise.race() :和Promise.all()方法类似,但只要有一个 Promise 对象的状态变为 resolved 或 rejected,它就会返回该 Promise 对象的结果。

    • Promise.resolve() :将对象转为状态为resolved的Promise对象(等价于new Promise(resolve => resolve()))

    • Promise.reject() :将对象转为状态为rejected的Promise对象(等价于new Promise((resolve, reject) => reject()))

javascript 复制代码
const myPromise = new Promise((resolve, reject) => {
  // 通过setTimeout模拟了一个耗时1秒的异步操作
  setTimeout(() => {
    const randomNumber = Math.random();
    if (randomNumber > 0.5) {
      // 操作成功,调用resolve函数
      resolve(randomNumber);
    } else {
      // 操作失败,调用reject函数
      reject(new Error('操作失败'));
    }
  }, 1000);
});

// 使用then方法处理Promise的结果
myPromise.then((result) => {
  console.log('操作成功:', result);
}).catch((error) => {
  console.log('操作失败:', error);
});
  • Promise.all()Promise.race()
javascript 复制代码
const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("Promise 1");
  }, 2000);
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("Promise 2");
  }, 1000);
});

const promise3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("Promise 3");
  }, 3000);
});

Promise.all([promise1, promise2, promise3])
  .then((results) => {
    console.log(results); // ['Promise 1', 'Promise 2', 'Promise 3']
  })
  .catch((error) => {
    console.error(error);
  });

Promise.race([promise1, promise2, promise3])
  .then((results) => {
    console.log(results); // Promise 2
  })
  .catch((error) => {
    console.error(error);
  });
  • Promise.resolve()Promise.reject()
javascript 复制代码
// 将一个值转化为 resolved 的 Promise 对象
const promise1 = Promise.resolve(42);
// 将一个值转化为 rejected 的 Promise 对象
const promise2 = Promise.reject(42);

promise1.then((result) => {
  console.log(result); // 输出:42
});

promise2.catch((error) => {
  console.log(error); // 输出:42
});

Symbol

  • 在ES6之前对象属性名都是字符串,这容易造成属性名的冲突。ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它属于 JavaScript 语言的原生数据类型之一。

  • Symbol是一种独一无二且不可修改的数据类型,可以用作对象属性的唯一标识符。它被设计用于创建对象属性的私有成员或者用作常量。

使用Symbol()函数可以创建一个新的Symbol。每次调用Symbol()函数都会返回一个全新且不相等的Symbol。

javascript 复制代码
const mySymbol1 = Symbol();
const mySymbol2 = Symbol('hhh');
console.log(typeof mySymbol1); // symbol
console.log(mySymbol2.toString()); // Symbol(hhh)
console.log(mySymbol2 == Symbol('hhh')); // false

Symbol可以作为对象的属性名来定义对象的私有成员。

javascript 复制代码
const obj = {};

const privateMember = Symbol();
obj[privateMember] = "私有成员";

console.log(obj[privateMember]); // "私有成员"

可以通过Object.getOwnPropertySymbols()方法获取对象的所有Symbol属性。

javascript 复制代码
const symbols = Object.getOwnPropertySymbols(obj);
console.log(symbols); // [Symbol()]
console.log(obj[symbols[0]]); // "私有成员"

Map/WeakMap和Set/WeakSet数据结构

Map

  • Map是一种键值对的集合(Hash 结构),它类似于对象,但有一些不同之处。Map的键可以是任意类型的值,包括对象和函数,而对象只能使用字符串作为键。此外,Map的键值对是有序的,插入顺序决定了键值对的顺序。

  • 方法

    • get() :返回键值对
    • set() :添加键值对,返回实例
    • delete() :删除键值对,返回布尔值
    • has() :检查键值对,返回布尔值
    • clear() :清除所有成员
    • keys() :返回以键为遍历器的对象
    • values() :返回以值为遍历器的对象
    • entries() :返回以键和值为遍历器的对象
    • forEach() :使用回调函数遍历每个成员
javascript 复制代码
// 创建一个空的Map
let map = new Map();

// 添加键值对
map.set('name', 'John');
map.set('age', 30);

// 获取值
console.log(map.get('name')); // John

// 检查是否包含某个键
console.log(map.has('age')); // true

// 删除键值对
map.delete('age');

//返回长度
console.log(map.size); // 1

WeakMap

  • WeakMap也是一种键值对的集合,但是只接受对象作为键,不接受其他类型的数据。WeakMap中的键是弱引用的,这意味着如果键对象没有其他引用,它会被垃圾回收机制回收,并且对应的键值对也会从WeakMap中被自动移除。

  • 应用

    • 储存DOM节点:DOM节点被移除时自动释放此成员键,不用担心这些节点从文档移除时会引发内存泄漏
    • 部署私有属性:内部属性是实例的弱引用,删除实例时它们也随之消失,不会造成内存泄漏
  • 方法

    • get() :返回键值对
    • set() :添加键值对,返回实例
    • delete() :删除键值对,返回布尔值
    • has() :检查键值对,返回布尔值

Set

  • Set是一种不重复值的集合,类似于数组,但是它的值是唯一的,不会重复。Set可以存储任意类型的值,包括原始类型和对象。

  • 方法

    • add() :添加值,返回实例
    • delete() :删除值,返回布尔值
    • has() :检查值,返回布尔值
    • clear() :清除所有成员
    • keys() :返回以属性值为遍历器的对象
    • values() :返回以属性值为遍历器的对象
    • entries() :返回以属性值和属性值为遍历器的对象
    • forEach() :使用回调函数遍历每个成员
javascript 复制代码
// 创建一个空的Set
let set = new Set();

// 添加值
set.add(1);
set.add(2);
set.add(2);
set.add(3);

// 检查是否包含某个值
console.log(set.has(2)); // true

// 删除值
set.delete(3);

//返回实例成员总数
console.log(set.size); // 2

WeakSet

  • WeakSet是一种弱引用集合,它只能存储对象类型的值,并且这些对象是弱引用的。这意味着如果一个对象在WeakSet中没有任何其他引用,那么这个对象将会被垃圾回收。由于WeakSet的成员是弱引用,因此无法迭代,也无法获取其中的大小或者清空它。

  • 应用

    • 储存DOM节点:DOM节点被移除时自动释放此成员键,不用担心这些节点从文档移除时会引发内存泄漏
    • 临时存放一组对象或存放跟对象绑定的信息 :只要这些对象在外部消失,它在WeakSet结构中的引用就会自动被垃圾回收
  • 方法

    • add() :添加值,返回实例
    • delete() :删除值,返回布尔值
    • has() :检查值,返回布尔值

迭代器(Iterator)和for...of

迭代器(Iterator)

  • 迭代器(Iterator)是一种迭代的机制,为各种不同的数据结构提供统一的访问机制。任何数据结构只要内部有 Iterator 接口,就可以完成依次迭代操作。
  • 默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是"可遍历的"。
  • 原生具备 Iterator 接口的数据结构如下:
    • Array
    • Map
    • Set
    • String
    • TypedArray
    • 函数的 arguments 对象
    • NodeList 对象
  • 迭代器对象方法:
    • next() :下一步操作,返回{ value,done }(必须部署)
    • return()for-of提前退出调用,返回{ done: true }
    • throw() :不使用,配合Generator函数使用
javascript 复制代码
let arr = ['a', 'b', 'c'];
let iter = arr[Symbol.iterator]();

iter.next() // { value: 'a', done: false }
iter.next() // { value: 'b', done: false }
iter.next() // { value: 'c', done: false }
iter.next() // { value: undefined, done: true }

for...of 循环

  • for...of循环用于遍历可迭代对象(例如数组、字符串、Set、Map等),它会迭代对象中的每个元素并执行指定的代码块。

使用for...of循环遍历数组:

javascript 复制代码
let arr = [1, 2, 3, 4];

for(let element of arr) {
  console.log(element);
}
// 输出:
// 1
// 2
// 3
// 4

使用for...of循环遍历字符串:

javascript 复制代码
let str = "Hello";

for(let char of str) {
  console.log(char);
}
// 输出:
// H
// e
// l
// l
// o

for...of循环遍历Set和Map的元素:

javascript 复制代码
let set = new Set([1, 2, 3]);

for(let value of set) {
  console.log(value);
}
// 输出:
// 1
// 2
// 3

let map = new Map([
  ['name', 'John'],
  ['age', 30]
]);

for(let [key, value] of map) {
  console.log(key, value);
}
// 输出:
// name John
// age 30

生成器(Generator)

  • Generator 函数在语法上,可以把它理解成一个状态机,内部封装了多个内部状态。

  • 执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。

  • 形式上,Generator 函数是一个普通函数,但是有两个特征:

    • function关键字与函数名之间有一个星号;
    • 函数体内部使用yield表达式,定义不同的内部状态
javascript 复制代码
function* helloWorldGenerator() {
  yield "hello";
  yield "world";
  return "ending";
}
var hw = helloWorldGenerator();
console.log(hw.next());// { value: 'hello', done: false }
console.log(hw.next());// { value: 'world', done: false }
console.log(hw.next());// { value: 'ending', done: true }
console.log(hw.next());// { value: undefined, done: true }

Proxy

  • Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种"元编程"(meta programming),即对编程语言进行编程。

  • Proxy 可以理解成,在目标对象之前架设一层"拦截 ",外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来"代理 "某些操作,可以译为"代理器"。

  • 使用:const proxy = new Proxy(target, handler)其中target 是拦截的目标对象,handler是定制拦截行为

  • 常见的拦截方式

    • get(target, propKey, receiver) :用于拦截某个属性的读取操作
    • set(target, propKey, value, receiver) :用来拦截某个属性的赋值操作,返回布尔值
    • has(target, propKey) :拦截对象属性检查k in obj,返回布尔值
    • deleteProperty(target, propKey) :拦截对象属性删除delete obj[k],返回布尔值
    • defineProperty(target, propKey, propDesc) :拦截对象属性定义Object.defineProperty()Object.defineProperties(),返回布尔值
    • ownKeys(target) :拦截对象属性遍历for-inObject.keys()Object.getOwnPropertyNames()Object.getOwnPropertySymbols(),返回数组
    • apply(target, object, args) :拦截Proxy实例作为函数调用proxy()proxy.apply()proxy.call()

Reflect

  • 在ES5及之前的版本中,对于对象的操作通常是通过Object的方法来完成,比如Object.defineProperty()Object.create()等。这些方法在使用上存在一些不一致和不直观的地方,比如使用Object.defineProperty()来定义属性时,如果属性已经存在,会抛出错误,而Reflect.defineProperty()则会返回false表示定义失败。

  • es6引入的Reflect是一个内置的对象,它提供了一组静态方法,用于操作对象。Reflect的方法和一些Object的方法具有相似的功能,但是使用Reflect方法可以更加简洁和直观;使用Reflect方法进行操作时,返回值可以告诉我们操作是否成功,而不是通过抛出错误来表示操作失败。

  • Reflect对象常用方法:

    • construct(target, argumentsList):用于创建一个类的实例对象。
    • get(target, propKey, receiver):获取对象的属性值。
    • set(target, propKey, value, receiver):设置对象的属性值。
    • has(target, propKey):判断对象是否具有某个属性。
    • deleteProperty(target, propKey):删除对象的属性。
    • apply(function, thisArg, args):调用一个函数,并传入指定的参数。
    • defineProperty(target, propKey, attributes):定义对象的属性。
  • 利用ReflectProxy写一个观察者模式

javascript 复制代码
class Observable {
  constructor() {
    this.data = {}; // 存储数据
    this.observers = new Set(); // 存储观察者
  }

  addObserver(observer) {
    this.observers.add(observer); // 添加观察者到observers集合中
  }

  removeObserver(observer) {
    this.observers.delete(observer); // 从observers集合中删除观察者
  }

  notifyObservers() {
    for (let observer of this.observers) {
      observer.update(this.data); // 通知观察者更新数据
    }
  }

  setData(key, value) {
    Reflect.set(this.data, key, value); // 使用Reflect设置数据
    this.notifyObservers(); // 通知观察者更新数据
  }
}

class Observer {
  constructor(name) {
    this.name = name; // 观察者名称
  }

  update(data) {
    console.log(`${this.name} received data:`, data); // 观察者接收到数据并输出
  }
}

// 创建可观察对象
const subject = new Observable();

// 创建观察者对象
const observer1 = new Observer("Observer 1");
const observer2 = new Observer("Observer 2");

// 将观察者对象添加到主题对象中
subject.addObserver(observer1);
subject.addObserver(observer2);

// 设置数据并通知观察者
subject.setData("name", "Jack");
subject.setData("age", 25);

// 输出结果:
// Observer 1 received data: { name: 'Jack' }
// Observer 2 received data: { name: 'Jack' }
// Observer 1 received data: { name: 'Jack', age: 25 }
// Observer 2 received data: { name: 'Jack', age: 25 }

数组对象扩展

  1. Array.prototype.from(): 将类数组对象或可迭代对象转换为数组。
javascript 复制代码
const arrayLike = { 0: 'a', 1: 'b', 2: 'c', length: 3 };
const array = Array.from(arrayLike);
console.log(array); // ['a', 'b', 'c']
  1. Array.prototype.of(): 根据传入的参数创建一个新数组。
javascript 复制代码
const array = Array.of(1, 2, 3);
console.log(array); // [1, 2, 3]
  1. Array.prototype.find(): 返回数组中满足条件的第一个元素。
javascript 复制代码
const array = [1, 2, 3, 4, 5];
const found = array.find(element => element > 3);
console.log(found); // 4
  1. Array.prototype.findIndex(): 返回数组中满足条件的第一个元素的索引。
javascript 复制代码
const array = [1, 2, 3, 4, 5];
const index = array.findIndex(element => element > 3);
console.log(index); // 3
  1. Array.prototype.fill(): 用指定的值填充数组。
javascript 复制代码
const array = [1, 2, 3, 4, 5];
array.fill(0, 1, 3);
console.log(array); // [1, 0, 0, 4, 5]
  1. Array.prototype.copyWithin():用于将数组中的一部分元素复制到指定位置,覆盖原有的元素。
javascript 复制代码
const arr = [1, 2, 3, 4, 5];
arr.copyWithin(0, 3); // 将索引为3及其之后的元素复制到索引为0的位置
console.log(arr); // 输出:[4, 5, 3, 4, 5]
  1. Array.prototype.keys():返回一个包含数组中每个索引的新Array Iterator对象。
javascript 复制代码
const arr = ['a', 'b', 'c'];
const iterator = arr.keys();
for (const key of iterator) {
  console.log(key); // 输出:0, 1, 2
}
  1. Array.prototype.values():该方法返回一个包含数组中每个元素的新Array Iterator对象。
javascript 复制代码
const arr = ['a', 'b', 'c'];
const iterator = arr.values();
for (const value of iterator) {
  console.log(value); // 输出:'a', 'b', 'c'
}
  1. Array.prototype.entries():返回一个包含数组中每个索引和对应元素的新Array Iterator对象。
javascript 复制代码
const arr = ['a', 'b', 'c'];
const iterator = arr.entries();
for (const [index, value] of iterator) {
  console.log(index, value); // 输出:0 'a', 1 'b', 2 'c'
}
  1. 数组空位 :ES6中对待数组空位的方式有所改变。空位指的是数组中某个位置没有任何值,例如[1, , 3]中的第二个元素是个空位。在ES6之前,对待数组空位的方式是跳过它们,不进行任何操作。但在ES6中,空位被视为undefined的值。例如,使用ES6的数组方法时,空位会被当作undefined处理。

字符串对象扩展

  1. String.prototype.includes():判断字符串是否包含指定的字符,返回布尔值。
javascript 复制代码
const str = "Hello, world!";
console.log(str.includes("world")); // 输出:true
console.log(str.includes("foo")); // 输出:false
  1. String.prototype.startsWith():判断字符串是否以指定的字符开始,返回布尔值。
javascript 复制代码
const str = "Hello, world!";
console.log(str.startsWith("Hello")); // 输出:true
console.log(str.startsWith("foo")); // 输出:false
  1. String.prototype.endsWith():判断字符串是否以指定的字符结束,返回布尔值。
javascript 复制代码
const str = "Hello, world!";
console.log(str.endsWith("world!")); // 输出:true
console.log(str.endsWith("foo")); // 输出:false
  1. String.prototype.repeat():将字符串重复指定次数,返回新的字符串。
javascript 复制代码
const str = "Hello, world!";
console.log(str.repeat(3)); // 输出:Hello, world!Hello, world!Hello, world!
  1. String.prototype.fromCodePoint():根据给定的码点创建一个字符串。它可以将一个或多个码点转换为对应的字符。
javascript 复制代码
console.log(String.fromCodePoint(65)); // 输出:A
console.log(String.fromCodePoint(97, 98, 99)); // 输出:abc
  1. String.prototype.raw():用于获取一个模板字符串的原始字符串形式,忽略其中的转义字符。
javascript 复制代码
const path = 'C:\\Users\\hhh\\Documents\\file.txt';
console.log(String.raw`The file is located at ${path}`); // 输出:The file is located at C:\Users\hhh\Documents\file.txt
  1. String.prototype.codePointAt():用于返回指定位置的字符的码点。
javascript 复制代码
const str = 'abc';
console.log(str.codePointAt(0)); // 输出:97
console.log(str.codePointAt(1)); // 输出:98
console.log(str.codePointAt(2)); // 输出:99
  1. String.prototype.normalize():用于将字符串的 Unicode 标准化形式。它主要用于处理 Unicode 字符串的不同表示方式。
javascript 复制代码
const str = 'c\u0327';
console.log(str.normalize()); // 输出:ç

Math对象扩展

  • 二进制表示法 : 0b或0B开头表示二进制(0bXX0BXX)

  • 二进制表示法 : 0b或0B开头表示二进制(0bXX0BXX)

  • 八进制表示法 : 0o或0O开头表示二进制(0oXX0OXX)

  • Number.EPSILON : 数值最小精度

  • Number.MIN_SAFE_INTEGER : 最小安全数值(-2^53)

  • Number.MAX_SAFE_INTEGER : 最大安全数值(2^53)

  • Number.parseInt() : 返回转换值的整数部分

  • Number.parseFloat() : 返回转换值的浮点数部分

  • Number.isFinite() : 是否为有限数值

  • Number.isNaN() : 是否为NaN

  • Number.isInteger() : 是否为整数

  • Number.isSafeInteger() : 是否在数值安全范围内

  • Math.trunc() : 返回数值整数部分

  • Math.sign() : 返回数值类型(正数1负数-1零0)

  • Math.cbrt() : 返回数值立方根

  • Math.clz32() : 返回数值的32位无符号整数形式

  • Math.imul() : 返回两个数值相乘

  • Math.fround() : 返回数值的32位单精度浮点数形式

  • Math.hypot() : 返回所有数值平方和的平方根

  • Math.expm1() : 返回e^n - 1

  • Math.log1p() : 返回1 + n的自然对数(Math.log(1 + n))

  • Math.log10() : 返回以10为底的n的对数

  • Math.log2() : 返回以2为底的n的对数

  • Math.sinh() : 返回n的双曲正弦

  • Math.cosh() : 返回n的双曲余弦

  • Math.tanh() : 返回n的双曲正切

  • Math.asinh() : 返回n的反双曲正弦

  • Math.acosh() : 返回n的反双曲余弦

  • Math.atanh() : 返回n的反双曲正切

Object对象扩展

  1. Object.is(value1, value2): 用于比较两个值是否相同。与"==="操作符的行为类似,但有两个区别:一是Object.is(NaN, NaN)返回true,而"==="操作符返回false;二是Object.is(+0, -0)返回false,而"==="操作符返回true。
javascript 复制代码
console.log(Object.is(1, 1)); // true
console.log(Object.is(NaN, NaN)); // true
console.log(Object.is(+0, -0)); // false
  1. Object.assign(target, ...sources): 将一个或多个源对象的属性复制到目标对象中。它返回目标对象。如果多个源对象具有相同的属性,则后面的对象的属性将覆盖前面的对象的属性。
javascript 复制代码
const target = { a: 1, b: 2 };
const source = { b: 3, c: 4 };
const result = Object.assign(target, source);
console.log(result); // { a: 1, b: 3, c: 4 }
  1. Object.getPrototypeOf(obj): 用于获取对象的原型。它返回指定对象的原型。
javascript 复制代码
const obj = {};
const prototype = Object.getPrototypeOf(obj);
console.log(prototype); // {}
  1. Object.setPrototypeOf(obj, prototype): 用于设置对象的原型。它将指定对象的原型设置为另一个对象或null。
javascript 复制代码
const obj = {};
const prototype = { a: 1 };
Object.setPrototypeOf(obj, prototype);
console.log(obj.a); // 1
  1. __proto__方法:用于获取或设置对象的原型。

正则对象扩展

  1. RegExp构造函数的扩展:在ES6之前,RegExp构造函数不允许使用第二个参数添加修饰符。在ES6中,如果RegExp构造函数第一个参数是一个正则对象,那么可以使用第二个参数指定修饰符。而且,返回的正则表达式会忽略原有的正则表达式的修饰符,只使用新指定的修饰符。
javascript 复制代码
console.log(new RegExp(/abc/ig, 'i').flags);// "i"
  1. Flags属性:用于返回正则表达式的修饰符。
javascript 复制代码
const regex = /abc/gi;
console.log(regex.flags); // "gi"
  1. u修饰符:用于处理大于\uFFFF的Unicode字符。
javascript 复制代码
console.log(/^\S$/.test('𠮷'));// false
console.log(/^\S$/u.test('𠮷'));// true
  1. y修饰符:也称为"粘连"修饰符,用于指定只从目标字符串的当前位置开始匹配。
javascript 复制代码
const regex = /abc/y;
console.log(regex.exec("abcabc")); // [ 'abc', index: 0, input: 'abcabc', groups: undefined ]
console.log(regex.exec("abcabc")); // [ 'abc', index: 3, input: 'abcabc', groups: undefined ] 因为第二次匹配从上一次匹配的结束位置开始
console.log(regex.exec("abcabc")); // null 
  1. RegExp.prototype.sticky 表示是否有y修饰符
  2. RegExp.prototype.unicode 表示是否有u修饰符
javascript 复制代码
console.log(/hello\d/y.sticky);//true
console.log(/hello\d/u.unicode);//true
  1. 正则方法调用变更 :字符串对象的match()replace()search()split()内部调用转为调用RegExp实例对应的RegExp.prototype[Symbol.方法]

ES7

ES7,也称为ECMAScript 2016,是ECMAScript的第7个版本。它于2016年发布,引入了一些新的语言特性和改进。

Array.prototype.includes()方法

  • includes()方法用于判断一个数组是否包含某个指定的元素,并返回布尔值。
javascript 复制代码
let arr = [1,2,3,4,6]
console.log(arr.includes(5));//false
console.log(arr.includes(6));//true

指数操作符

  • ES7引入了指数运算符,用于计算一个数的指数,**具有与 Math.pow(..)等效的计算结果。
javascript 复制代码
console.log(2**3);//8

ES8

ES8,也称为ECMAScript 2017,是ECMAScript(JavaScript)的第八个版本。它于2017年6月发布,并在ES6(ES2015)的基础上引入了一些新特性和语法改进。

async/await

  • ES8引入了async/await语法,它是一种更简洁、更易于理解和编写异步代码的方式。它建立在Promise的基础上,通过使用async关键字来定义一个异步函数,以及使用await关键字来等待一个Promise对象的解决。

  • 使用async关键字定义的函数会返回一个Promise对象,而在函数体内使用await关键字可以暂停函数的执行,直到等待的Promise对象解决为止。这样可以使得异步代码的执行顺序更加线性,避免了回调地狱。

  • 当使用Promise时,链式调用太多的话代码通常会变得比较冗长和嵌套:

javascript 复制代码
// 定义一个延迟函数,接受一个毫秒数作为参数
function delay(ms) {
  // 返回一个新的Promise对象,该对象在指定的毫秒数后解决
  return new Promise((resolve) => setTimeout(resolve, ms));
}

// 延迟1秒后执行的操作
delay(1000)
  .then(() => {
    console.log("After 1 second"); // 输出"After 1 second"
    return delay(2000); // 返回一个延迟2秒的Promise对象
  })
  .then(() => {
    console.log("After 2 seconds"); // 输出"After 2 seconds"
    return "Finished"; // 返回一个字符串"Finished"
  })
  .then((result) => {
    console.log(result); // 输出"Finished"
  })
  .catch((error) => {
    console.error(error); // 输出捕获到的错误信息(如果有)
  });
  • 相比之下,使用async/await可以使代码更加线性和易于理解,以一种更接近同步代码的方式编写异步操作:
javascript 复制代码
// 定义一个延迟函数,接受一个毫秒数作为参数
function delay(ms) {
  // 返回一个新的Promise对象,该对象在指定的毫秒数后解决
  return new Promise((resolve) => setTimeout(resolve, ms));
}

// 定义一个异步函数example
async function example() {
  console.log("Start"); // 输出"Start"
  await delay(1000); // 等待1秒钟
  console.log("After 1 second"); // 输出"After 1 second"
  await delay(2000); // 等待2秒钟
  console.log("After 2 seconds"); // 输出"After 2 seconds"
  return "Finished"; // 返回一个字符串"Finished"
}

// 定义一个异步函数runExample,用于执行example函数
async function runExample() {
  try {
    const result = await example(); // 等待example函数完成并获取结果
    console.log(result); // 输出example函数的返回值
  } catch (error) {
    console.error(error); // 输出捕获到的错误信息(如果有)
  }
}

runExample(); // 执行runExample函数,开始执行异步操作

Object.entries()

  • Object.entries()方法返回一个给定对象自身可枚举属性的键值对数组
javascript 复制代码
let obj = {a:1,b:2}
for (let [key, value] of Object.entries(obj)) {
    console.log(`${key}: ${value}`)
}
//输出
// a: 1
// b: 2

Object.values()

  • Object.values()方法返回一个给定对象自身可枚举属性值的数组
javascript 复制代码
let obj = {a:1,b:2}
console.log(Object.values(obj));//[ 1, 2 ]

Object.getOwnPropertyDescriptors()

  • Object.getOwnPropertyDescriptors() 方法用来获取一个对象的所有自身属性的描述符
javascript 复制代码
let obj = {a:1,b:2}
console.log(Object.getOwnPropertyDescriptors(obj));
//输出
// {
//     a: { value: 1, writable: true, enumerable: true, configurable: true },
//     b: { value: 2, writable: true, enumerable: true, configurable: true }
// }

padStart()和padEnd()

  • padStart()方法可以在字符串的开头添加指定的字符,直到字符串达到指定的长度。如果字符串的长度已经达到或超过了指定的长度,则不会进行任何填充。
javascript 复制代码
let phoneNumber = "18912345677"; // 电话号码
let paddedPhoneNumber = phoneNumber.slice(-4).padStart(11, "*"); // 将电话号码填充到11位,使用*进行填充,并且只保留后四位数
console.log(paddedPhoneNumber); // *******5677
  • padEnd()方法与padStart()方法类似,不同之处在于它在字符串的结尾添加指定的字符,直到字符串达到指定的长度。
javascript 复制代码
const text = 'Hello';
console.log(text.padEnd(8, '!')); // 输出:'Hello!!!'

ShareArrayBuffer(因安全问题,暂时在Chrome,FireFox,Safari中被禁用)

  • 用来表示一个通用的,固定长度的原始二进制数据缓冲区,类似于 ArrayBuffer 对象,它们都可以用来在共享内存(shared memory)上创建视图。与 ArrayBuffer 不同的是,SharedArrayBuffer 不能被分离。

ES9

ES9是ECMAScript的第9个版本,也被称为ES2018。它于2018年6月发布,引入了一些新的特性和语法改进。

for await...of

  • for await...of 是 ES9 中引入的一种语法,用于遍历异步迭代器的元素。它的语法类似于传统的 for...of 循环,但可以在异步操作完成后继续迭代下一个元素。

  • 利用Generator函数实现一个异步迭代器对象,然后用for await...of遍历这个异步迭代器的元素:

javascript 复制代码
async function* asyncGenerator() {
  yield Promise.resolve(1);
  yield Promise.resolve(2);
  yield Promise.resolve(3);
}

(async () => {
  for await (const num of asyncGenerator()) {
    console.log(num);
  }
})();

Rest/Spread 属性

  • Rest/Spread 属性语法是对扩展运算符的一种扩展。Rest/Spread 属性允许我们在对象字面量中使用扩展运算符(...)来获取对象的剩余属性或将属性扩展到另一个对象中。

  • Rest 属性用于从对象中提取剩余的属性,并将它们作为新的对象返回。这样可以方便地从一个对象中提取所需的属性,而不必一个一个地进行赋值。:

javascript 复制代码
const { name, age, ...rest } = { name: 'hhh', age: 18, sex: '男', city: '上海' };
console.log(name); // "hhh"
console.log(age); // 18
console.log(rest); // { sex: '男', city: '上海 }
  • Spread 属性用于将一个对象的属性扩展到另一个对象中。这样可以方便地合并两个对象的属性,而不必一个一个地进行赋值。
javascript 复制代码
const obj1 = { name: 'hhh', age: 18 };
const obj2 = { sex: '男', city: '上海' };
const mergedObj = { ...obj1, ...obj2 };
console.log(mergedObj); // { name: 'hhh', age: 18, sex: '男', city: '上海' }

Promise.finally()

  • Promise结束的时候,不管是结果是resolved还是rejected,都会调用finally中的方法, finally中的回调函数不接受任何参数
javascript 复制代码
const promise = new Promise((resolve, reject) => {
  // 异步操作
  // resolve(value); // 或 reject(reason);
});

promise
  .then(result => {
    // 处理成功的情况
  })
  .catch(error => {
    // 处理失败的情况
  })
  .finally(() => {
    // 无论成功或失败都会执行的回调函数
  });

正则表达式扩展

反向断言

  • 反向断言使用(?<=pattern)的语法来定义,其中pattern是一个子表达式,表示需要存在的模式。它可以用于匹配前面是某种模式的字符串。
javascript 复制代码
const regex = /(?<=\$)\d+/;
const match = regex.exec('The price is $99');
console.log(match[0]); // 输出:99
  • 反向否定断言是一种用于匹配在某种模式之后不存在的字符的方法。它使用(?!pattern)的语法来表示。这个模式会匹配一个位置,这个位置后面的字符不能匹配给定的模式。
javascript 复制代码
const str = "a123bbcd667";
const regex = /(?<![a-z])\d+/g;
const matches = str.match(regex);
console.log(matches); // 输出: [ '23', '67' ]

命名捕获组

  • 命名捕获组允许我们给正则表达式的子表达式(也称为分组)命名,并且可以通过名称来引用它们的匹配结果。

  • 命名捕获组可以通过在子表达式前面加上?<name>的语法来实现。

javascript 复制代码
const regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = regex.exec('2023-08-28');

console.log(match.groups.year); // 输出:2023
console.log(match.groups.month); // 输出:08
console.log(match.groups.day); // 输出:28

s修饰符(dotAll模式)

  • 在默认情况下,正则表达式中的点字符(.)匹配除换行符(\n)之外的任何字符。但是,使用 dotAll 模式后,点字符将匹配包括换行符在内的任何字符。
javascript 复制代码
const regex = /a.b/s;
const str = "a\nb";
console.log(regex.test(str)); // 输出 true

Unicode属性转义

  • Unicode 属性转义用于匹配具有特定属性的字符。这些转义的形式分别是 \p{...}\P{...}\p{...} 是一个 Unicode 属性转义,用于匹配具有指定属性的字符。它的语法是 \p{Property=Value},其中 Property 是 Unicode 属性的名称,Value 是该属性的取值。通过使用这个转义,可以在正则表达式中匹配具有特定属性的字符。
javascript 复制代码
const reEmoji = /\p{Emoji}/u;
console.log(reEmoji.test('🥰😊😍')); // 输出 true

ES10

ES10是ECMAScript的第10个版本,也被称为ES2019。它于2019年6月发布,引入了一些新的特性和语法改进。

数组扁平化方法

  • Array.prototype.flat()方法会按照一个可指定的深度遍历递归数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
javascript 复制代码
const arr = [[1, 2], [3, [4, 5]]];
console.log(arr.flat(1)); // 输出: [1, 2, 3, [4, 5]]

const deeplyNestedArr = [[1, [2, [3, [4, [5]]]]]];
console.log(deeplyNestedArr.flat(Infinity)); // 输出: [1, 2, 3, 4, 5]
  • Array.prototype.flatMap()方法结合了 map()flat() 两个方法的功能。它首先对数组的每个元素执行一个映射函数,然后将结果扁平化为一维数组。
javascript 复制代码
const arr = [1, 2, 3, 4, 5];
console.log(arr.flatMap(num => [num * num])); // 输出: [1, 4, 9, 16, 25]

const words = ["Hello", "World"];
console.log(words.flatMap(word => word.split(""))); // 输出: ["H", "e", "l", "l", "o", "W", "o", "r", "l", "d"]

字符串去除开头和结尾的空格方法

  • String.prototype.trimStart() 方法去除字符串开头的空白字符,返回一个新的字符串,原始字符串不受影响。空白字符包括空格、制表符、换行符等。
javascript 复制代码
const str = "   Hello, world!   ";
console.log(str.trimStart()); // "Hello, world!   "
  • String.prototype.trimEnd() 方法去除字符串结尾的空白字符,返回一个新的字符串,原始字符串不受影响。
javascript 复制代码
const str = "   Hello, world!   ";
console.log(str.trimEnd()); // "   Hello, world!"

Object.fromEntries

  • Object.fromEntries()用于将一个包含键值对的可迭代对象(如Array,Map等对象)转换为一个新的对象。
javascript 复制代码
const entries = [['name', 'hhh'], ['age', 25], ['city', '上海']];
const obj = Object.fromEntries(entries);
console.log(obj); // { name: 'hhh', age: 25, city: '上海' }

Symbol.prototype.description

  • Symbol.prototype.description用于获取Symbol的描述信息。
javascript 复制代码
console.log(Symbol('myDescription').description);//myDescription

Function.prototype.toString()

  • 之前的版本中,该方法来自 Object.prototype.toString(),得到的字符串是去掉空白符号的。
  • 但在 ES10 中,得到的字符串会保留空格和注释,如果函数是内置方法的,或者是通过 bind() 方法创建的,Function.prototype.toString() 方法将返回一个标记为 [native code] 的字符串。
javascript 复制代码
function sum(a, b) {
    return a + b;
}

console.log(sum.toString())
// "function sum(a, b) {
// 		return a + b;
//  }"
let newSum = sum.bind();
console.log(newSum.toString());// "function () { [native code] }"
console.log(Math.abs.toString()) // "function abs() { [native code] }"

catch 绑定

  • 在 ES10 中,允许在 catch 语句中绑定错误对象,而不需要通过 catch 语句的参数来引用它。
  • 在 ES10 之前,catch 语句通常如下所示:
javascript 复制代码
try {
  // 一些可能抛出错误的代码
} catch (error) {
  // 处理错误的代码
}

在 ES10 中,可以使用 catch 绑定来进行错误处理,如下所示:

javascript 复制代码
try {
  // 一些可能抛出错误的代码
} catch {
  // 处理错误的代码
}

JSON.stringify() 增强

  • 在 ES10 中,JSON.stringify() 方法修复了之前版本中对于一些超出范围的 Unicode 字符的展示错误的问题。
  • 在修复之前,当遇到超出 U+FFFF 的 Unicode 字符时,JSON.stringify() 方法会将其转义为一个由两个代理字符组成的字符串,而不是正确的 Unicode 字符。
  • 修复后,JSON.stringify() 方法会正确地展示这些超出范围的 Unicode 字符。这个修复使得对于 Unicode 字符的处理更加准确和方便。
javascript 复制代码
console.log(JSON.stringify('🥰')); // "🥰"

ES11

ES11是ECMAScript的第11个版本,也被称为ES2020。它于2020年6月发布,引入了一些新的特性和语法改进。

globalThis

  • 在之前的 JavaScript 版本中,全局对象的名称是不一致的,比如在浏览器中是 window,在 Node.js 中是 global。这导致了在跨平台开发时需要针对不同的环境使用不同的全局对象。

  • 为了解决这个问题,ES11 引入了 globalThis,它是一个统一的全局对象。无论在浏览器还是 Node.js 中,都可以使用 globalThis 来访问全局对象。这样,你就不需要根据不同的环境来手动切换全局对象的名称了。

BigInt

  • 在之前的版本中,整数的范围受到限制,超过 2^53 或小于 -2^53 的整数会丢失精度。而 BigInt 类型通过添加后缀 n 或调用构造函数 BigInt() 来表示超出这个范围的整数,并且可以进行算术运算。

  • 要注意的是:

    • BigIntMath对象中的方法不可用;
    • BigIntNumber实例不能混合运算,需要转换为相同类型;
    • BigInt在转换为Number时可能会丢失精度;
    • 使用BigInt进行带小数的运算会向下取整;
    • BigIntNumber不是严格相等,但是宽松相等。
javascript 复制代码
const maxSafeInteger = BigInt(Number.MAX_SAFE_INTEGER);
const bigNumber = BigInt("123456789012345678901234567890");

console.log(maxSafeInteger); // 9007199254740991n
console.log(bigNumber); // 123456789012345678901234567890n

const sum = maxSafeInteger + bigNumber;
console.log(sum); // 123456789012345678901234576981n

console.log(2n > 2,2n > 1);// false true
console.log(0n === 0,0n == 0);// false true

可选链操作符

  • 可选链操作符(Optional Chaining Operator)用于简化访问嵌套对象属性或方法时的安全性检查。通过?.来判断属性或方法是否存在,如果存在则访问,如果不存在则返回undefined,避免了在访问链中出现的类型错误或未定义错误。

  • 在ES11之前,如果要访问嵌套对象属性或方法时,我们需要手动进行安全性检查,以避免出现错误:

javascript 复制代码
// 在ES11之前的代码
if (obj && obj.prop && obj.prop.method) {
  // 访问obj.prop.method
}
  • 使用可选链操作符,可以简化上述代码,避免了多次的检查和重复的代码。
javascript 复制代码
// 使用可选链操作符
if (obj?.prop?.method) {
  // 访问obj.prop.method
}

空值合并操作符

  • 空值合并操作符(Nullish Coalescing Operator)用于在给定的一组值中选择一个非空(非null和非undefined)的值。

  • 在以前的版本中,我们通常使用逻辑或运算符(||)来实现类似的功能。但是逻辑或运算符有一个问题,它会将假值(例如空字符串''、数字0、布尔值false等)也视为"空",从而导致错误的结果。

  • 而空值合并操作符(??)只在左侧的值为null或undefined时才会选择右侧的值,对于其他的假值不会触发选择右侧的值。

  • 注意:不可以将 ?? 与 AND(&&)OR(||)混用,会报错。

javascript 复制代码
// 在ES11之前的代码
const value = x || defaultValue;

// 使用空值合并操作符
const value = x ?? defaultValue;

String.prototype.matchAll()

  • String.prototype.matchAll() 返回一个包含所有匹配正则表达式的迭代器对象。迭代器对象可以通过 for...of 循环或者 Array.from() 方法转换为数组,并且每个元素都是一个包含匹配结果的数组。
javascript 复制代码
const str = 'Hello, world!';
const regex = /[a-z]/gi;
const matches = str.matchAll(regex);

for (const match of matches) {
  console.log(match);
}
//输出
// [ 'H', index: 0, input: 'Hello, world!', groups: undefined ]
// [ 'e', index: 1, input: 'Hello, world!', groups: undefined ]
// [ 'l', index: 2, input: 'Hello, world!', groups: undefined ]
// [ 'l', index: 3, input: 'Hello, world!', groups: undefined ]
// [ 'o', index: 4, input: 'Hello, world!', groups: undefined ]
// [ 'w', index: 7, input: 'Hello, world!', groups: undefined ]
// [ 'o', index: 8, input: 'Hello, world!', groups: undefined ]
// [ 'r', index: 9, input: 'Hello, world!', groups: undefined ]
// [ 'l', index: 10, input: 'Hello, world!', groups: undefined ]
// [ 'd', index: 11, input: 'Hello, world!', groups: undefined ]

import()

  • ES11 中引入了 import() 函数,它是一个动态导入模块的方法。在以前的 ES6 模块系统中,所有的导入语句都必须在代码的头部静态编译,不能根据运行时的条件来导入不同的模块。而 import() 函数允许在代码运行时根据需要动态地加载模块。

  • import() 函数返回一个Promise,该 Promise 在模块加载完成后被解析为一个包含模块的默认导出的对象。这使得我们可以在需要的时候延迟加载模块,从而提高应用程序的性能和响应速度。

javascript 复制代码
import('./module.js')
  .then((module) => {
    // 使用模块中的内容
    module.default();
  })
  .catch((error) => {
    // 处理加载模块失败的情况
    console.error('模块加载失败', error);
  });

Promise.allSettled()

  • Promise.allSettled() 方法接收一个由 Promise 对象组成的可迭代对象(比如数组),并返回一个新的 Promise 对象。这个新的 Promise 对象在所有传入的 Promise 对象都已经被解决(settled)后才会被解决。

  • Promise.all() 不同的是,即使其中的某个 Promise 被拒绝(rejected),Promise.allSettled() 仍会等待所有 Promise 对象都被解决,然后返回一个包含每个 Promise 对象结果的数组。这使得我们能够获取所有 Promise 对象的状态,而不必担心其中某个 Promise 对象的拒绝(rejected)会中断整个操作。

javascript 复制代码
const promises = [
  Promise.resolve("Resolved"),
  Promise.reject("Rejected"),
  Promise.resolve("Resolved"),
];

Promise.allSettled(promises).then((results) => {
  results.forEach((result) => {
    if (result.status === "fulfilled") {
      console.log(`Promise resolved: ${result.value}`);
    } else if (result.status === "rejected") {
      console.log(`Promise rejected: ${result.reason}`);
    }
  });
});

ES12

ES12,也被称为ES2021,于2021年6月发布。ES12引入了一些新的特性和改进,以提升JavaScript语言的功能和性能。

数值分隔符

  • 数值分隔符(Numeric Separators)允许在数字中使用下划线 _ 进行分隔,以提高数字的可读性。数值分隔符在 JavaScript 中是可选的,它们不会影响数字的值或计算结果。

  • 使用数值分隔符,可以将长数字分成更易读的部分:

javascript 复制代码
const billion = 1_000_000_000;
console.log(billion); // 输出 1000000000
  • 数值分隔符可以在整数和浮点数中使用,但不能在数字的开头或结尾使用,也不能在小数点前后使用。
javascript 复制代码
const number = 1_234.567_89;
console.log(number); // 输出 1234.56789

逻辑赋值运算符

  • 逻辑赋值运算符(Logical Assignment Operators)是一种简化常见逻辑操作和赋值的合并运算符。

  • ES12中引入了三个逻辑赋值运算符:

  1. ||=:逻辑或赋值运算符。如果左侧的操作数为假(例如,undefinednull、false 或 0),则将右侧的操作数赋值给左侧的变量。
javascript 复制代码
let x = 0;
x ||= 5;
console.log(x); // 输出 5,因为 x 是假值,所以将 5 赋值给 x
  1. &&=:逻辑与赋值运算符。如果左侧的操作数为真(例如,非空字符串、非零数字或对象),则将右侧的操作数赋值给左侧的变量。
javascript 复制代码
let y = 10;
y &&= 7;
console.log(y); // 输出 7,因为 y 是真值,所以将 7 赋值给 y
  1. ??=:空值合并赋值运算符。如果左侧的操作数为 nullundefined,则将右侧的操作数赋值给左侧的变量。
javascript 复制代码
let z = null;
z ??= 3;
console.log(z); // 输出 3,因为 z 是空值,所以将 3 赋值给 z

String.prototype.replaceAll()

  • String.prototype.replaceAll()方法,该方法可以用指定的字符串替换字符串中的所有匹配项。在之前的版本中,我们只能使用正则表达式配合replace()方法来实现替换所有匹配项的操作。

  • 使用 replaceAll() 方法非常简便,只需要传入两个参数:第一个参数是要替换的字符串或正则表达式(必须是全局的),第二个参数是替换后的字符串。方法会返回一个新的字符串,其中所有匹配项都被替换为指定的字符串。

javascript 复制代码
console.log("Hello, world!".replaceAll("o", "x")); // "Hellx, wxrld!"
console.log("Hello, world!".replaceAll(/l/g, "x")); // "Hexxo, worxd!"

Promise.any()

  • Promise.any() 方法接收一个 Promise 对象的可迭代参数(如数组),并返回一个新的 Promise 对象。

  • 这个新的 Promise 对象将在其中的任何一个 Promise 对象变为 fulfilled(已完成)状态时解析,并以该 Promise 对象的解析值作为其解析值。如果可迭代参数中的所有 Promise 对象都变为 rejected(已拒绝)状态,则返回的 Promise 对象将会被拒绝,并以一个 AggregateError 实例作为拒绝值。

javascript 复制代码
const promise1 = Promise.resolve(1);
const promise2 = Promise.reject(2);
const promise3 = Promise.resolve(1);

Promise.any([promise1, promise2, promise3])
  .then((value) => {
    console.log(value); // 1
  })
  .catch((error) => {
    // 全部请求失败时才会执行这里的代码
    console.log(error);
  });

小结

以上是我整理的ES6到ES12的常用新特性,本人水平有限,如有错误欢迎在评论区指正,一起讨论!(๑•̀ㅂ•́)و✧

相关推荐
也无晴也无风雨25 分钟前
深入剖析输入URL按下回车,浏览器做了什么
前端·后端·计算机网络
Martin -Tang1 小时前
Vue 3 中,ref 和 reactive的区别
前端·javascript·vue.js
SRY122404192 小时前
javaSE面试题
java·开发语言·面试
FakeOccupational2 小时前
nodejs 020: React语法规则 props和state
前端·javascript·react.js
放逐者-保持本心,方可放逐3 小时前
react 组件应用
开发语言·前端·javascript·react.js·前端框架
曹天骄4 小时前
next中服务端组件共享接口数据
前端·javascript·react.js
阮少年、4 小时前
java后台生成模拟聊天截图并返回给前端
java·开发语言·前端
不二人生5 小时前
SQL面试题——连续出现次数
hive·sql·面试
郝晨妤5 小时前
鸿蒙ArkTS和TS有什么区别?
前端·javascript·typescript·鸿蒙
AvatarGiser6 小时前
《ElementPlus 与 ElementUI 差异集合》Icon 图标 More 差异说明
前端·vue.js·elementui