(ES6+)随笔
本文主要记录一些关于( ES6+ )的历史背景、应用场景、版本变动、新的属性和方法、类、模块化和一些其他内容,供自己以后查漏补缺,也欢迎同道朋友交流学习。
历史背景
ES6
的历史背景可以追溯到 1996
年,当时 JS 的创造者 Netscape
公司决定将 JS 提交给国际标准化组织 ECMA
,希望这种语言能够成为国际标准。次年,ECMA 发布了 262 号标准文件(ECMA-262
)的第一版,规定了浏览器脚本语言的标准,并将这种语言称为 ECMAScript
,这个版本就是 1.0
版。
在 1997
年至 1999
年间,ECMAScript 经历了 2.0
和 3.0
两个版本的迭代。其中,ECMAScript 3.0 版是一个巨大的成功,在业界得到广泛支持,成为通行标准,奠定了JavaScript语言的基本语法。
2000
年,ECMAScript 4.0
开始酝酿,这个版本最后没有通过,但是它的大部分内容被 ES6 继承了。因此,可以说 ES6 制定的起点其实是 2000 年。ECMAScript 4.0之所以没有通过,是因为这个版本太激进了,对 ES3 做了彻底升级,导致标准委员会的一些成员不愿意接受。
2008
年 7 月,由于对于下一个版本应该包括哪些功能,各方分歧太大,争论过于激烈,ECMA决定中止 ECMAScript 4.0 的开发,将其中涉及现有功能改善的一小部分发布为 ECMAScript 3.1
,而将其他激进的设想扩大范围,放入以后的版本,这个版本的项目代号起名为Harmony(和谐)。会后不久,ECMAScript 3.1就改名为 ECMAScript 5
。
2009
年 12 月,ECMAScript 5.0 版正式发布。Harmony项目一分为二,一些较为可行的设想定名为 JavaScript.next
继续开发,后来演变成 ECMAScript 6。
2013
年 3 月,ECMAScript 6
草案冻结,不再添加新功能。2015 年 6 月,ECMAScript 6
正式通过,成为国际标准。从2000年算起,这时已经过去了15年,也被称为 ECMAScript 2015
。
2015
年以来,JS 语言规范每年都会发布新版本,引入新的语言特性和改进。
应用场景
ES6
及其后续版本引入了众多新特性,极大地改进了 JS 语言的能力,提高了开发效率和代码质量。以下是一些 ES6 在现代 Web 开发中的主要应用场景
:
- 模块化编程:使用ES6的模块化特性,开发者可以创建结构清晰、易于维护的前端代码库。
- 异步编程:利用
async
和await
语法,简化了异步操作和回调函数的管理,改善了代码的可读性和错误处理。 - 面向对象编程:通过
class
关键字,ES6提供了更简洁的语法来实现面向对象编程,包括继承、封装等特性。 - 函数式编程:
箭头函数
和数组的高阶函数(如map
、filter
、reduce
)支持函数式编程范式。 - 简化代码:使用
${}
模板字符串、参数解构赋值、展开运算符等语法,可以更简化代码编写流程。
版本变动
自 ES6(ECMAScript 2015
)以来,JS 语言规范每年都会发布新版本,引入新的语言特性和改进。"ES6+"
通常是指 ES6 之后的所有 ECMAScript 版本,包括 ES7(2016)
、ES8(2017)
、ES9(2018)
、ES10(2019)
,以及后续的年度版本。以下是一些 ES6+ 引入的关键特性的简要概述:
-
ES7 (2016):
- 引入了
Array.prototype.includes
方法,用于检查数组中是否存在特定元素。 - 新增了指数运算符(
**
)。
- 引入了
-
ES8 (2017):
- 引入了
async
/await
语法,简化了异步编程。 - 新增了
Object.values
和Object.entries
方法。 - 增加了
String.prototype.padStart
和String.prototype.padEnd
方法。
- 引入了
-
ES9 (2018):
- 引入了异步迭代器和
for-await-of
循环。 - 新增了
Promise.finally
方法。 - 增加了对正则表达式命名捕获组的支持。
- 引入了异步迭代器和
-
ES10 (2019):
- 引入了
Array.prototype.flat
和Array.prototype.flatMap
方法,用于扁平化数组。 - 新增了
Object.fromEntries
方法。 - 增加了
String.prototype.trimStart
和String.prototype.trimEnd
方法。
- 引入了
-
ES11 (2020):
- 引入了可选链操作符(
?.
),简化了访问深层嵌套对象属性的语法。 - 新增了空值合并运算符(
??
)。
- 引入了可选链操作符(
-
ES12 (2021):
- 引入了逻辑赋值运算符,如
||=
、&&=
、??=
等。 - 新增了
Promise.allSettled
的支持。
- 引入了逻辑赋值运算符,如
-
ESNext:
- ECMAScript 的持续发展,每年都会有新的提案和特性加入,如
class
字段和方法的提案,新的国际化 API,以及对 JavaScript 引擎性能的持续优化。
- ECMAScript 的持续发展,每年都会有新的提案和特性加入,如
新的变量声明方式
ES6 引入了两种新的变量声明方式:let
和 const
。这些声明方式与之前的 var
声明方式相比,提供了块级作用域
和不可变性
等特点。
let
声明
let
用于声明一个块级作用域的变量,其值可以被重新赋值。- 在同一作用域内,不能重复声明同一个变量。
- 存在暂时性死区,变量声明要在使用之前。
let age;
// 不能重复声明
// let age; // SyntaxError: Identifier 'age' has already been declared
age = 30;
console.log(age); // 输出 30
// 暂时性死区
// Uncaught ReferenceError: Cannot access 'name' before initialization
name = 'Tom';
let name;
const
声明
const
用于声明一个块级作用域的常量,一旦声明并初始化后,其值不能被重新赋值。- 对于
基本数据类型
(如数字、字符串、布尔值),这意味着它们的值不可变; - 对于
复合数据类型
(如对象、数组),这意味着引用不可变,但对象或数组的内容可以变。
const age = 30;
// age = 40; // 尝试重新赋值会报错
const obj = { key: 'value' };
obj.key = 'newValue'; // 允许修改对象的内容
与 var
的对比
var
声明的变量具有函数作用域或全局作用域,而不是块级作用域。var
声明的变量可以在同一作用域内重复声明。
使用 let
和 const
可以避免 var
可能引起的一些作用域相关的问题,如变量提升(hoisting)和全局变量污染。
箭头函数
ES6 箭头函数(Arrow Function
)是 JS 中的一种新的函数定义方式,提供了更简洁的语法和自动绑定 this
的特性。以下是箭头函数的一些关键特点:
- 简洁的语法:箭头函数使用一个箭头
=>
代替了传统的function
关键字。
// 箭头函数
const multiply = (x, y) => x * y;
- 隐式返回:如果箭头函数的函数体只有一条语句,可以使用隐式返回,即省略花括号
{}
和return
关键字。
const double = x => x * 2;
console.log(double(10)); // 输出 20
-
没有
this
关键字:箭头函数没有自己的this
上下文,它会捕获其所在上下文的this
值,作为自己的this
。 -
没有
arguments
对象:箭头函数不能使用arguments
对象,如果需要使用类似的功能,可以使用剩余参数(rest parameters)代替。 -
不适用于构造函数:箭头函数不能用作构造函数,也就是说,不能用
new
关键字来调用箭头函数。 -
不绑定
call
、apply
和bind
方法:由于箭头函数没有自己的this
上下文,所以call
、apply
和bind
方法对箭头函数无效。
默认参数和模板字符串
- 默认参数:在函数中,可以使用默认参数语法来为函数参数提供默认值。
- 模板字符串:在字符串中,可以使用模板字符串语法来插入变量。
function greet(name = 'World') {
// 使用模板字符串去做拼接
console.log(`Hello, ${name}!`);
}
greet(); // "Hello, World!"
greet('niunai'); // "Hello, niunai!"
展开运算符和解构赋值
- 展开运算符:在数组、字符串和函数参数中,可以使用展开运算符
...
将一个数组、字符串或其他可迭代对象中的元素展开。 - 解构赋值:在数组和对象中,可以使用解构赋值语法将数组或对象的元素赋值给变量。
const arr = [1, 2, 3];
const [a, ...b] = arr;
console.log(a); // 1
console.log(b); // [2, 3]
const obj = { x: 1, y: 2, z: 3 };
const { x, ...y } = obj;
console.log(x); // 输出 1 2
console.log(y); // {y: 2, z: 3}
类 (class)
ES6 中的 class
关键字是 JavaScript 中实现面向对象编程的一种新方式。虽然它看起来类似于其他面向对象语言中的类,但实际上,它是基于 JavaScript 现有的原型继承体系构建的语法糖。以下是 ES6 类的几个关键特性:
类的使用
// 使用 `class` 关键字声明一个新的类。
class Person {
// 使用 `constructor` 方法初始化类的对象。
// 当使用 `new` 关键字创建类的新实例时,会自动调用构造函数。
constructor(name, age) {
// 在构造函数中定义的属性是实例属性,每个实例都有自己的属性副本。
this.name = name;
this.age = age;
}
// 方法定义
greet() {
console.log(`Hello, my name is ${this.name}!`);
}
// 静态方法
static info() {
console.log('This is a static method.');
}
}
// 调用静态方法
Person.info();
类的继承
使用 extends
关键字实现类的继承。子类可以继承并扩展父类的功能。
class Employee extends Person {
constructor(name, age, jobTitle) {
super(name, age); // 调用父类的构造函数
this.jobTitle = jobTitle;
}
jobDescription() {
super.info() // 调用父类的方法
console.log(`${this.name} is a ${this.jobTitle}.`);
}
}
getter 和 setter
使用 get
和 set
关键字定义属性的 getter
和 setter
,允许在读取或设置属性值时添加额外的逻辑。
class Person {
// ...
get age() {
return this._age;
}
set age(value) {
if (value > 0) {
this._age = value;
}
}
}
- 私有属性和方法:虽然 ES6 不直接支持私有属性,但可以使用符号(Symbols)或闭包来模拟私有属性。
模块化
ES6 模块化是 JS 语言的一项重大改进,它提供了一种新的组织代码的方式,允许开发者将功能分割成独立的模块,然后根据需要导入和导出这些模块。以下是 ES6 模块化的一些关键特性:
导出
使用 export
关键字从模块中导出函数、类、表达式或声明(变量、函数、类等)。
// math.js
export function add(x, y) {
return x + y;
}
export const PI = 3.14;
// 在每个模块中,可以有一个默认导出,它使用 `default` 关键字标记。
const tools = {}
export default tools
导入
使用 import
关键字从其他模块导入导出的内容。
// 按需导入
import { add, PI } from './math.js';
console.log(add(1, 2)); // 3
console.log(PI); // 3.14
// 默认导入
import defaultFunction from './utils.js';
defaultFunction();
// 重命名导入
import * as MathUtils from './math.js';
MathUtils.add(2, 3);
// 动态导入
if (someCondition) {
import('./module.js')
.then(module => {
module.doSomething();
})
.catch(err => {
console.error('Module failed to load', err);
});
}
模块化的好处
- 封装性:模块可以封装代码和数据,避免全局命名空间的污染。
- 可维护性:模块化代码更容易理解和维护。
- 可重用性:模块可以在不同的项目中重复使用。
- 依赖管理:模块之间的依赖关系更加清晰。
ES6 模块化是现代 JavaScript 开发的基础,它使得代码更加模块化、可维护和可扩展。随着前端工程化的发展,ES6 模块化已经成为大型前端项目的标准实践。
异步编程和Promise
ES6 引入了 Promise
作为异步编程的一种解决方案,它提供了一种新的方式来处理异步操作,使得异步代码的编写更加清晰和易于管理。
Promise
Promise
是一个代表异步操作最终完成或失败的对象。它有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。
// 创建 Promise
const myPromise = new Promise((resolve, reject) => {
// 异步操作
if (/* 异步操作成功 */) {
resolve(value); // 操作成功,执行 resolve 回调
} else {
reject(error); // 操作失败,执行 reject 回调
}
});
// 使用 Promise
myPromise
.then(value => {
// 处理异步操作的返回值
})
.catch(error => {
// 处理异步操作中的错误
});
Promise 的链式调用
-
Promise 的链式调用:
then()
方法返回一个新的Promise
对象,这允许你将多个Promise
调用链接在一起,形成链式调用。fetch('https://api.example.com/data') .then(response => response.json()) .then(data => { // 处理获取的数据 }) .catch(error => { // 处理请求或数据处理中的错误 });
Promise 的静态方法
-
Promise.all()
:当需要等待多个异步操作都完成时,Promise.all()
方法可以接收一个Promise
数组作为参数,只有当所有的Promise
都成功时,它才会解决。Promise.all([promise1, promise2, promise3]).then(results => { // 所有 promise 都成功时执行 });
-
Promise.race()
:Promise.race()
方法同样是接收一个Promise
数组,但它会立即解决,只要数组中任何一个Promise
完成(无论是成功还是失败)。Promise.race([promise1, promise2]).then(result => { // 最先完成的 promise 会决定结果 });
Async/Await
-
async
和await
:async
关键字用于声明一个异步函数,它使得函数内部的异步代码可以用同步的方式书写。await
关键字用于等待一个Promise
解决,暂停函数的执行,直到Promise
完成。async function fetchData() { try { const response = await fetch('https://api.example.com/data'); const data = await response.json(); // 使用数据 } catch (error) { // 处理错误 } }
Map、WeakMap
Map
和 WeakMap
是 ES6 引入的两种集合类型,它们提供了存储键值对的方式,类似于传统的对象({}
),但它们提供了一些额外的功能和灵活性。
Map
Map
是一个集合,它存储键值对的数组,其中键可以是任意类型。
- 键可以是任意类型,包括对象、函数等。
Map
对象保持键的插入顺序。
// 创建 Map
const map = new Map();
// 添加元素
map.set(key, value);
// 获取元素
const value = map.get(key);
// 检查 Map 中是否存在键
const hasKey = map.has(key);
// 删除元素
map.delete(key);
// 遍历 Map*
map.forEach((value, key) => {
console.log(key, value);
});
WeakMap
WeakMap
是一个收集键值对的集合,与 Map
类似,但它的键必须是对象,而且这些对象的引用是弱引用,意味着如果对象没有其他的引用,它们可以被垃圾回收器回收。
- 键必须是对象。
- 没有
size
属性,因为垃圾回收器可以随时回收只有WeakMap
引用的对象。 - 不可遍历,没有
forEach
方法。
// 创建 WeakMap
const weakMap = new WeakMap();
// 添加元素
weakMap.set(keyObject, value);
// 获取元素
const value = weakMap.get(keyObject);
// 检查 WeakMap 中是否存在键
const hasKey = weakMap.has(keyObject);
// 删除元素
weakMap.delete(keyObject);
Map和WeakMap的应用场景
Map
和 WeakMap
是两种不同的集合类型,它们在 JS 中有着各自的应用场景:
-
Map 的应用场景:
- 需要有序的键值对存储:
Map
保持元素的插入顺序,这在需要有序遍历键值对时非常有用。 - 键可以是任意类型:与普通对象不同,
Map
的键可以是任何值,包括对象、函数或任何原始类型。 - 需要大量键值对:当对象的键数量非常大时,使用
Map
可以避免潜在的性能问题,因为对象的键作为字符串处理可能会影响性能。 - 需要快速查找键是否存在:
Map
提供了.has()
方法,可以快速检查一个键是否存在于Map
中。 - 需要删除键值对:使用
Map
可以方便地使用.delete()
方法删除特定的键值对。 - 需要遍历键值对:
Map
提供了.forEach()
方法,可以遍历所有键值对,这在需要对键和值执行操作时非常有用。 - 需要大小可变的集合:
Map
的大小可以动态变化,适合需要根据运行时数据调整集合大小的场景。
- 需要有序的键值对存储:
-
WeakMap 的应用场景:
- 需要自动垃圾回收的键:
WeakMap
的键是弱引用,如果键在其他地方没有被引用,那么垃圾回收器可以回收这些键所占用的内存。 - 管理内存敏感的场景:当需要缓存大量对象时,使用
WeakMap
可以减少内存泄漏的风险。 - 私有数据存储:
WeakMap
可以用来存储对象的私有数据,而不污染对象本身,因为WeakMap
不可迭代,外部代码无法访问到存储的数据。 - 事件处理器或回调的存储:当需要为对象关联事件处理器或回调,并且希望这些处理器或回调在对象被销毁时自动清理时,
WeakMap
是一个很好的选择。 - 缓存和性能优化:
WeakMap
可以用作缓存实现,当缓存的键对象不再被使用时,缓存可以被自动清理。 - 避免循环引用:在某些情况下,对象之间可能形成循环引用,使用
WeakMap
可以避免这种循环引用导致的内存泄漏问题。
- 需要自动垃圾回收的键:
Set、WeakSet
ES6 中的 Set
和 WeakSet
提供了不同的集合功能,以下是它们的使用方式和一些示例。
Set 的使用
Set
是一种新的集合类型,它存储唯一的值,无论是原始类型还是对象。
// 创建一个 Set
const mySet = new Set();
// 添加元素
mySet.add(1);
mySet.add('text');
mySet.add({ name: 'Tom' });
// 检查 Set 中是否包含某个元素
console.log(mySet.has(1)); // true
// 获取 Set 的大小
console.log(mySet.size); // 3
// 移除 Set 中的元素
mySet.delete('text');
// 遍历 Set
mySet.forEach((value) => {
console.log(value);
});
// 数组去重
const numbers = [1, 2, 2, 3, 4, 4, 5];
const uniqueNumbers = [...new Set(numbers)];
console.log(uniqueNumbers); // [1, 2, 3, 4, 5]
WeakSet 的使用
WeakSet
是一种只能存储对象引用的集合,且这些引用是弱引用,意味着如果对象没有被其他地方引用,它可以被垃圾回收。
基本用法:
// 创建一个 WeakSet
const myWeakSet = new WeakSet();
// 添加元素到 WeakSet,元素必须是对象
const myObject = {};
myWeakSet.add(myObject);
// WeakSet 不提供 has 方法,因此无法直接检查对象是否存在
// 移除元素
myWeakSet.delete(myObject);
// WeakSet 不可迭代,因此无法直接遍历
Set和WeakSet使用场景对比
- 使用
Set
存储一组不重复的数据,可以是任何类型,适合需要明确元素存在性检查和遍历的场景。 - 使用
WeakSet
存储对象引用,适合不需要长期持有对象引用,或希望自动管理内存的场景。
Set和WeakSet注意事项
Set
是可迭代的,而WeakSet
不是。这意味着你不能使用forEach
或for...of
循环遍历WeakSet
。WeakSet
只能存储对象,不能存储null
或原始类型。WeakSet
中的对象引用是弱引用,因此WeakSet
本身不阻止垃圾回收器回收这些对象。
Symbol 和私有属性
Symbol
是 ES6 引入的一种新的原始数据类型,用于创建唯一的、不可变的(immutable
)值,通常用作对象属性的键。使用 Symbol
可以创建一个私有属性,因为 Symbol
值不能被猜测或意外地访问。
// 创建 Symbol
const mySymbol = Symbol('mySymbol');
// 作为对象属性的键
const myObject = {
[mySymbol]: 'This is a private property'
};
// 访问 Symbol 属性
console.log(myObject[mySymbol]); // 输出: This is a private property
// Symbol 唯一性
const uniqueSymbol = Symbol('unique');
console.log(uniqueSymbol === Symbol('unique')); // 输出:false
私有属性的使用
在 ES6 之前,JavaScript 对象并没有真正的私有属性概念。但是,使用 Symbol
可以模拟私有属性:
-
定义私有属性: 通过将属性的键设置为
Symbol
,可以使得属性在对象外部不可见或不可访问。const privateKey = Symbol('privateKey'); class MyClass { constructor() { this[privateKey] = 'I am a private property'; } } const myInstance = new MyClass(); // console.log(myInstance[privateKey]); // 引用错误,privateKey 在外部是不可访问的
-
在类中访问私有属性:在类的内部,可以通过
this[Symbol]
访问私有属性。 -
私有属性的继承:子类无法访问父类的
Symbol
属性,这提供了一种额外的封装层。class ChildClass extends MyClass { showPrivate() { console.log(this[privateKey]); // 引用错误,privateKey 在子类中不可访问 } }
Symbol注意事项
Symbol
作为属性键不会显示在for...in
循环或Object.keys()
中。Symbol
属性不能通过Object.getOwnPropertyNames()
获取,但可以通过Object.getOwnPropertySymbols()
获取。
迭代器和生成器
在 ES6 中,迭代器
和生成器
是两个重要的概念,它们改变了我们处理数据结构的方式以及编写异步代码的方法。下面我将详细介绍这两个概念及其基本使用方式。
迭代器 (Iterator)
迭代器
是一种遍历集合中元素的对象。每个迭代器都有一个 next()
方法,用于获取序列中的下一个值。这个方法返回一个包含两个属性的对象:value
和 done
。其中 value 是当前元素的值,而 done 是一个布尔值,指示是否已经到达序列的末尾
。
// 创建一个简单的迭代器
const simpleIterator = {
count: 0,
next: function() {
if (this.count < 3) {
this.count++;
return { value: this.count, done: false };
} else {
return { done: true };
}
}
};
// 使用迭代器
let result = simpleIterator.next();
while (!result.done) {
console.log(result.value);
result = simpleIterator.next();
}
生成器 (Generator)
生成器
是一种特殊的函数,它允许你创建迭代器。生成器函数使用 function*
语法声明,并且可以包含一个或多个 yield
表达式。yield 表达式使得生成器函数可以“挂起
”其执行过程,并在之后通过调用 next() 方法恢复执行。
生成器函数的特性包括:
- 使用 function* 语法声明。
- yield 表达式用于产生值并暂停函数执行。
- 生成器函数返回一个迭代器。
function* simpleGenerator() {
yield 1;
yield 2;
yield 3;
}
const gen = simpleGenerator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }
迭代器与生成器的结合
生成器
通常被用来创建迭代器
。生成器函数通过 yield 表达式来产生值,这些值可以被迭代器返回。
function* numberGenerator(limit) {
for (let i = 1; i <= limit; i++) {
yield i * i;
}
}
const squareNumbers = numberGenerator(5);
for (let num of squareNumbers) {
console.log(num); // 输出 1, 4, 9, 16, 25
}
Proxy 和 Reflect
在 ES6中,Proxy
和 Reflect
是两个非常有用但相对高级的概念,它们提供了强大的功能来拦截和控制对象的行为。
Proxy
Proxy
是一种对象,它可以作为目标对象的代理
。当你尝试访问、修改或者执行某些操作在一个 Proxy 对象上时,你可以定义一组拦截器来改变或观察这些操作的行为。
Proxy 的创建需要两个参数:一个是目标对象
,另一个是处理器对象(handler
)。处理器对象定义了各种拦截行为,例如读取属性、设置属性、构造函数调用等。
// 创建 Proxy
const target = {};
const handler = {
get: function(target, prop, receiver) {
console.log(`Fetching ${prop}`);
return Reflect.get(target, prop, receiver);
},
set: function(target, prop, value, receiver) {
console.log(`Setting: ${prop} = ${value}`);
return Reflect.set(target, prop, value, receiver);
}
};
const proxy = new Proxy(target, handler);
常见的 Proxy 方法
get
: 当读取一个属性时触发。set
: 当设置一个属性时触发。apply
: 当调用一个函数时触发。construct
: 当使用 new 调用一个构造函数时触发。has
: 当使用 in 操作符时触发。deleteProperty
: 当删除一个属性时触发。ownKeys
: 获取所有属性键时触发。getOwnPropertyDescriptor
: 获取属性描述符时触发。defineProperty
: 定义或修改一个属性时触发。preventExtensions
: 当尝试阻止对象扩展时触发。getPrototypeOf
: 获取原型时触发。setPrototypeOf
: 设置原型时触发。
Reflect
Reflect
是一个内置的对象,它提供了一系列静态方法来直接操作目标对象。这些方法与 Proxy 处理器方法相对应,可以让你在不使用 Proxy 的情况下执行相同的操作。
Reflect 的设计目的是为了使 JavaScript 的行为更加一致和可预测,并且减少了语言中的“魔法”部分。
常见的 Reflect 方法
Reflect.get(target, propertyKey[, receiver])
: 获取属性值。Reflect.set(target, propertyKey, value[, receiver])
: 设置属性值。Reflect.apply(function, thisArg, argsArray)
: 调用函数。Reflect.construct(constructor, argsArray[, newTarget])
: 构造新对象。Reflect.has(target, propertyKey)
: 检查属性是否存在。Reflect.deleteProperty(target, propertyKey)
: 删除属性。Reflect.ownKeys(target)
: 获取所有属性键。Reflect.getOwnPropertyDescriptor(target, propertyKey)
: 获取属性描述符。Reflect.defineProperty(target, propertyKey, attributes)
: 定义或修改属性。Reflect.preventExtensions(target)
: 防止对象扩展。Reflect.getPrototypeOf(target)
: 获取原型。Reflect.setPrototypeOf(target, prototype)
: 设置原型。
Proxy和Reflect的作用
Proxy
主要用于拦截操作,而Reflect
则用于执行这些操作。Proxy
使得我们可以定义自定义的行为模式,而Reflect
则提供了一种标准化的方式来执行这些行为。- 两者经常配合使用,以实现更复杂的对象行为控制。
ES6新增的对象方法
从ES6开始,JavaScript引入了多个新特性来增强对象的操作,以下是一些在ES6及之后版本中新增或改进的对象方法:
- 属性的简写:当对象的键名和对应的值名相同时,可以
省略值名
,只写键名
。 - 属性名表达式:允许使用表达式作为对象的属性名,需要将表达式放在方括号内。
- super关键字:指向当前对象的原型对象,只能在对象的方法中使用 。
- 扩展运算符:允许从一个对象中取出所有可枚举的属性,并将它们复制到另一个对象中 。
- 属性的遍历:ES6提供了多种方法来遍历对象的属性,包括
for...in
循环、Object.keys()
、Object.getOwnPropertyNames()
、Object.getOwnPropertySymbols()
和Reflect.ownKeys()
。 Object.is()
:一个新方法,用来比较两个值是否严格相等,与===
运算符相似,但+0
不等于-0
,NaN
等于自身。Object.assign()
:用于对象的合并,将源对象的所有可枚举属性复制到目标对象 。Object.getOwnPropertyDescriptors()
:返回指定对象所有自身属性的描述对象 。Object.setPrototypeOf()
和Object.getPrototypeOf()
:分别用来设置和获取一个对象的原型对象 。Object.keys()
,Object.values()
,Object.entries()
:分别返回一个数组,包含对象自身的所有可枚举属性的键名、值和键值对 。Object.fromEntries()
:将键值对数组转换为一个对象 。Object.hasOwn()
:检查对象是否具有指定的自有属性 。
ES6新增的数组方法
ES6 为 Array
对象新增了一些有用的方法,使得数组的操作更加简洁和高效。以下是一些在 ES6 中引入的数组方法:
-
Array.from()
:用于将类数组对象(如NodeList
)或可迭代对象(如Set
、Map
)转换成真正的数组。const divs = document.querySelectorAll('div'); const arrayFromDivs = Array.from(divs);
-
Array.of()
:用于创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型。const array = Array.of(1, 2, 3); // [1, 2, 3]
-
find()
:返回数组中满足提供的测试函数的第一个元素的值,如果没有找到则返回undefined
。const users = [{ name: 'Tom', age: 30 }, { name: 'Tony', age: 25 }]; const user = users.find(user => user.age === 25); // { name: 'Tony', age: 25 }
-
findIndex()
:返回数组中满足提供的测试函数的第一个元素的索引,如果没有找到则返回-1
。const index = users.findIndex(user => user.name === 'Tom'); // 0
-
fill()
:用一个固定值填充一个数组从开始到结束(或到指定位置)的元素。const array = [1, 2, 3]; array.fill(4); // [4, 4, 4]
-
copyWithin()
:将数组的一部分复制到数组的另一个位置,覆盖或替换现有元素。const array = [1, 2, 3, 4, 5]; array.copyWithin(0, 3); // [4, 5, 3, 4, 5]
-
扩展的
slice()
:slice()
方法现在可以接受负数参数,表示从数组末尾开始计算的位置。const array = [1, 2, 3, 4, 5]; const lastTwo = array.slice(-2); // [4, 5]
-
扩展的
splice()
:splice()
方法现在可以接受更多的参数,包括用于替换的元素。const array = [1, 2, 3, 4]; array.splice(2, 1, 'a', 'b'); // [1, 2, 'a', 'b', 4]
-
includes()
:用于判断数组是否包含某个值,返回true
或false
。const array = [1, 2, 3]; const contains = array.includes(2); // true
-
flat()
和flatMap()
:flat()
方法用于将数组的嵌套结构(多维数组)扁平化为一维数组。flatMap()
方法首先使用映射函数映射每个元素,然后对返回的数组进行扁平化。const array = [1, [2, 3], 4]; const flatArray = array.flat(); // [1, 2, 3, 4] const array = [1, 2, 3]; const flatMapped = array.flatMap(element => [element, element + 1]); // [1, 2, 2, 3, 3, 4]