ES是什么?
ES是ECMAScript的缩写,也就是JavaScript的标准化规范。ECMAScript是一种由Ecma国际组织制定的脚本语言标准,它定义了JavaScript的语法、类型、操作符、对象和函数等基本组件。ES6(也称为ES2015)是ECMAScript的第六个版本,引入了许多新的语言特性和改进,如箭头函数、模板字面量、解构赋值等。随后的版本(如ES7、ES8等)也引入了许多新的功能和语法糖,以进一步改进和扩展JavaScript。
时间轴
各版本特性总结
版本 | 特性 | 描述 |
---|---|---|
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;
扩展操作符
-
扩展操作符用于展开可迭代对象(如数组、字符串等),将其元素逐个展开,以便于在函数调用、数组字面量、对象字面量等地方使用。
-
在使用扩展操作符时,你需要在要展开的可迭代对象前面加上三个点(...)。
- 展开数组:
javascript
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]
- 传递参数给函数:
javascript
function sum(a, b, c) {
return a + b + c;
}
const numbers = [1, 2, 3];
const result = sum(...numbers); // 6
- 浅拷贝数组或对象:
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的解构赋值语法允许我们从数组或对象中提取值,并将它们赋给变量。这使得我们可以更简洁地进行变量赋值操作。
-
解构赋值可以用于数组和对象。下面是一些示例:
- 数组解构赋值:
javascript
let numbers = [1, 2, 3];
let [a, b, c] = numbers;
console.log(a); // 输出 1
console.log(b); // 输出 2
console.log(c); // 输出 3
- 对象解构赋值:
javascript
let person = { name: 'John', age: 20 };
let { name, age } = person;
console.log(name); // 输出 'John'
console.log(age); // 输出 20
除了基本的数组和对象解构赋值外,ES6的解构赋值还提供了一些其他的操作。
- 剩余项(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]
- 解构赋值还支持默认值,当解构的值为undefined时,会使用默认值:
javascript
let person = { name: 'John' };
let { name, age = 18 } = person;
console.log(name); // 输出 'John'
console.log(age); // 输出 18
- 嵌套解构:可以在解构赋值中嵌套使用数组和对象的解构。例如:
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
- 对象属性别名:可以为解构赋值的变量设置别名,使用冒号来指定别名。例如:
javascript
let person = { name: 'John', age: 20 };
let { name: fullName, age } = person;
console.log(fullName); // 输出 'John'
console.log(age); // 输出 20
- 解构赋值还可以在函数参数中使用,方便地提取函数参数中的值:
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并没有原生的模块化支持。开发人员通常使用一些第三方库或模式来实现模块化,例如
CommonJS
和AMD
。 -
然而,ES6引入了原生的模块化系统,通过
import
和export
关键字来实现。下面是一个示例:
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 :将状态从
进行中
变为失败
,在异步操作失败时调用,并将异步操作的错误作为参数传递出去
- resolve :将状态从
-
主要方法:
-
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函数
使用
- next() :下一步操作,返回
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-in
、Object.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):定义对象的属性。
-
利用
Reflect
和Proxy
写一个观察者模式
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 }
数组对象扩展
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']
Array.prototype.of()
: 根据传入的参数创建一个新数组。
javascript
const array = Array.of(1, 2, 3);
console.log(array); // [1, 2, 3]
Array.prototype.find()
: 返回数组中满足条件的第一个元素。
javascript
const array = [1, 2, 3, 4, 5];
const found = array.find(element => element > 3);
console.log(found); // 4
Array.prototype.findIndex()
: 返回数组中满足条件的第一个元素的索引。
javascript
const array = [1, 2, 3, 4, 5];
const index = array.findIndex(element => element > 3);
console.log(index); // 3
Array.prototype.fill()
: 用指定的值填充数组。
javascript
const array = [1, 2, 3, 4, 5];
array.fill(0, 1, 3);
console.log(array); // [1, 0, 0, 4, 5]
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]
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
}
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'
}
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'
}
- 数组空位 :ES6中对待数组空位的方式有所改变。空位指的是数组中某个位置没有任何值,例如
[1, , 3]
中的第二个元素是个空位。在ES6之前,对待数组空位的方式是跳过它们,不进行任何操作。但在ES6中,空位被视为undefined的值。例如,使用ES6的数组方法时,空位会被当作undefined处理。
字符串对象扩展
String.prototype.includes()
:判断字符串是否包含指定的字符,返回布尔值。
javascript
const str = "Hello, world!";
console.log(str.includes("world")); // 输出:true
console.log(str.includes("foo")); // 输出:false
String.prototype.startsWith()
:判断字符串是否以指定的字符开始,返回布尔值。
javascript
const str = "Hello, world!";
console.log(str.startsWith("Hello")); // 输出:true
console.log(str.startsWith("foo")); // 输出:false
String.prototype.endsWith()
:判断字符串是否以指定的字符结束,返回布尔值。
javascript
const str = "Hello, world!";
console.log(str.endsWith("world!")); // 输出:true
console.log(str.endsWith("foo")); // 输出:false
String.prototype.repeat()
:将字符串重复指定次数,返回新的字符串。
javascript
const str = "Hello, world!";
console.log(str.repeat(3)); // 输出:Hello, world!Hello, world!Hello, world!
String.prototype.fromCodePoint()
:根据给定的码点创建一个字符串。它可以将一个或多个码点转换为对应的字符。
javascript
console.log(String.fromCodePoint(65)); // 输出:A
console.log(String.fromCodePoint(97, 98, 99)); // 输出:abc
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
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
String.prototype.normalize()
:用于将字符串的Unicode
标准化形式。它主要用于处理Unicode
字符串的不同表示方式。
javascript
const str = 'c\u0327';
console.log(str.normalize()); // 输出:ç
Math对象扩展
-
二进制表示法
:0b或0B开头
表示二进制(0bXX
或0BXX
) -
二进制表示法
:0b或0B开头
表示二进制(0bXX
或0BXX
) -
八进制表示法
:0o或0O开头
表示二进制(0oXX
或0OXX
) -
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对象扩展
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
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 }
Object.getPrototypeOf(obj)
: 用于获取对象的原型。它返回指定对象的原型。
javascript
const obj = {};
const prototype = Object.getPrototypeOf(obj);
console.log(prototype); // {}
Object.setPrototypeOf(obj, prototype)
: 用于设置对象的原型。它将指定对象的原型设置为另一个对象或null。
javascript
const obj = {};
const prototype = { a: 1 };
Object.setPrototypeOf(obj, prototype);
console.log(obj.a); // 1
__proto__方法
:用于获取或设置对象的原型。
正则对象扩展
RegExp构造函数的扩展
:在ES6之前,RegExp构造函数不允许使用第二个参数添加修饰符。在ES6中,如果RegExp
构造函数第一个参数是一个正则对象,那么可以使用第二个参数指定修饰符。而且,返回的正则表达式会忽略原有的正则表达式的修饰符,只使用新指定的修饰符。
javascript
console.log(new RegExp(/abc/ig, 'i').flags);// "i"
Flags属性
:用于返回正则表达式的修饰符。
javascript
const regex = /abc/gi;
console.log(regex.flags); // "gi"
u修饰符
:用于处理大于\uFFFF
的Unicode字符。
javascript
console.log(/^\S$/.test('𠮷'));// false
console.log(/^\S$/u.test('𠮷'));// true
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
RegExp.prototype.sticky
表示是否有y
修饰符RegExp.prototype.unicode
表示是否有u
修饰符
javascript
console.log(/hello\d/y.sticky);//true
console.log(/hello\d/u.unicode);//true
- 正则方法调用变更 :字符串对象的
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()
来表示超出这个范围的整数,并且可以进行算术运算。 -
要注意的是:
BigInt
在Math
对象中的方法不可用;BigInt
与Number
实例不能混合运算,需要转换为相同类型;BigInt
在转换为Number
时可能会丢失精度;- 使用
BigInt
进行带小数的运算会向下取整; BigInt
和Number
不是严格相等,但是宽松相等。
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中引入了三个逻辑赋值运算符:
||=
:逻辑或赋值运算符。如果左侧的操作数为假(例如,undefined
、null
、false 或 0),则将右侧的操作数赋值给左侧的变量。
javascript
let x = 0;
x ||= 5;
console.log(x); // 输出 5,因为 x 是假值,所以将 5 赋值给 x
&&=
:逻辑与赋值运算符。如果左侧的操作数为真(例如,非空字符串、非零数字或对象),则将右侧的操作数赋值给左侧的变量。
javascript
let y = 10;
y &&= 7;
console.log(y); // 输出 7,因为 y 是真值,所以将 7 赋值给 y
??=
:空值合并赋值运算符。如果左侧的操作数为null
或undefined
,则将右侧的操作数赋值给左侧的变量。
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的常用新特性,本人水平有限,如有错误欢迎在评论区指正,一起讨论!(๑•̀ㅂ•́)و✧