ES6
Reflect
Reflect 是 ES6 中新增的一个内置对象,它提供了一组与对象操作相关的方法,这些方法与 Object 上的方法类似,但有一些不同之处。它的作用是让一些 Object 上的操作变成函数式的操作,比如 delete 操作符变成了 Reflect.deleteProperty() 方法。Reflect 的方法都是静态方法,也就是说,不需要通过实例来调用。
Reflect 提供了一些常见的操作方法,如:Reflect.get(),Reflect.set(),Reflect.has(),Reflect.deleteProperty(),Reflect.construct() 等。这些方法与对应的 Object 方法的作用类似,但是它们具有一些不同的特性,比如返回值和执行方式等。
Reflect 还提供了一些其他方法,如:Reflect.apply(),Reflect.defineProperty(),Reflect.getOwnPropertyDescriptor(),Reflect.getPrototypeOf() 等,它们也都与 Object 上的方法类似,但是具有一些不同的特性。
示例:
- 使用
Reflect.get()获取对象的属性值:
const person = { name: 'Alice', age: 25 };
const age = Reflect.get(person, 'age');
console.log(age); // 25- 使用
Reflect.set()设置对象的属性值:
const person = { name: 'Alice', age: 25 };
Reflect.set(person, 'age', 26);
console.log(person); // { name: 'Alice', age: 26 }- 使用
Reflect.has()检查对象是否包含某个属性:
const person = { name: 'Alice', age: 25 };
const hasAge = Reflect.has(person, 'age');
const hasAg = Reflect.has(person, 'ag');
console.log(hasAge); // true
console.log(hasAg); // false- 使用
Reflect.deleteProperty()删除对象的属性:
const person = { name: 'Alice', age: 25 };
Reflect.deleteProperty(person, 'age');
console.log(person); // { name: 'Alice' }- 使用
Reflect.construct()创建一个对象实例:
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
const person = Reflect.construct(Person, ['Alice', 25]);
console.log(person); // Person { name: 'Alice', age: 25 }- 使用
Reflect.apply()调用一个函数并传递参数:
const sayHi = (name) => {
console.log(`Hi, ${name}`);
}
Reflect.apply(sayHi, null, ['Alice']); // Hi, Alice
// 带 this
function sayHi(){
console.log(`Hi, ${this.name}`);
}
Reflect.apply(sayHi, {name: 'Alice'}, []); // Hi, AliceReflect 的出现让一些操作变得更加简单和易于理解,并且它的方法可以更好地支持“函数式编程”和“元编程”,使得开发者能够更加灵活地编写 JavaScript 代码。
Symbol
ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,是一种类似于字符串的数据类型。
Symbol 特点:
Symbol的值是唯一的,用来解决命名冲突的问题;Symbol值不能与其他数据进行运算;Symbol定义的对象属性不能使用for...in循环遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名。
基本使用:
const q = Symbol();
console.log(q); // Symbol()
// 带标识
const q1 = Symbol('qiuxc');
const q2 = Symbol('qiuxc');
console.log(q1, q2); // Symbol(qiuxc) Symbol(qiuxc)
// 一样的标识进行比较也不相等
console.log(q1 === q2); // false
// Symbol.for() 创建,具有唯一性
const q3 = Symbol.for('qiuxc');
const q4 = Symbol.for('qiuxc');
console.log(q3, q4); // Symbol(qiuxc) Symbol(qiuxc)
// Symbol.for() 创建的一样的标识进行比较则相等
console.log(q3 === q4); // true
// Symbol.keyFor() 获取 Symbol 类型值对应的字符串键名
const sym1 = Symbol.for('foo');
// 使用 Symbol.for 创建的具有唯一性,可以获取到键名
console.log(Symbol.keyFor(sym1)); // 输出"foo"
// 使用 Symbol 创建的不具有唯一性,无法获取到键名
const sym2 = Symbol('bar');
console.log(Symbol.keyFor(sym2)); // 输出undefined
// 不能与其他数据进行运算
q1 + 1 // Error
q1 > 1 // Error
q1 + q1 // Error
q1 + 'abc' // Error对象添加 Symbol 类型的属性:
// 安全地向对象中添加属性或方法,无需担心键冲突
const game = {
up(){
console.log('👆');
},
down(){
console.log('👇');
}
};
const methods = {
up: Symbol('up'),
down: Symbol('down')
};
game[methods.up] = function(){
console.log('向上');
}
game[methods.down] = function(){
console.log('向下');
}
console.log(game); // {up: ƒ, down: ƒ, Symbol(up): ƒ, Symbol(down): ƒ}Symbol 内置方法
Symbol 自带了 11 个静态方法,如下表:
| 方法名 | 作用 |
|---|---|
| Symbol.hasInstance | 当其他对象使用 instanceof 运算符,判断是否为该对象的实例时,会调用这个方法。 |
| Symbol.isConcatSpreÏadable | 对象的 Symbol.isConcatSpreadable 属性是一个布尔值,表示该对象用于 Array.prototype.concat() 时,是否可以展开。 |
| Symbol.match | 当执行 str.match(myObject) 时,如果该方法存在,会调用它,返回该方法的返回值。 |
| Symbol.replace | 当对象被 str.replace(myObject) 方法调用时,会返回该方法的返回值。 |
| Symbol.search | 当对象被 str.search(myObject) 方法调用时,会返回该方法的返回值。 |
| Symbol.split | 当对象被 str.split(myObject) 方法调用时,会返回该方法的返回值 |
| Symbol.species | 用于指定创建派生对象的构造函数。 |
| Symbol.toPrimitive | 该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。 |
| Symbol.toStringTag | 在该对象上面调用 toString 方法时,返回该方法的返回值。 |
| Symbol.iterator | 对象进行 for...of 循环时,会调用 Symbol.iterator 方法,返回该对象的默认遍历器。 |
| Symbol.unscopables | 用于指示某些属性是否应该被 with 语句排除在外。 |
以下是一些使用 Symbol 内置方法的示例:
- 使用
Symbol.hasInstance方法自定义对象的instanceof行为:
/**
* 在下面的示例中,Person 类定义了 Symbol.hasInstance 静态方法,该方法用于自定义对象的 instanceof 行为。
* 在该示例中,如果对象是一个包含 name 和 age 属性的普通对象,则认为它是 Person 类的实例。
* 因此,person 对象使用 instanceof 运算符检查是否为 Person 类的实例时,返回 true。
*/
class Person {
static [Symbol.hasInstance](obj) {
if (typeof obj !== 'object') {
return false;
}
return 'name' in obj && 'age' in obj;
}
}
const person = { name: 'Alice', age: 25 };
console.log(person instanceof Person); // 输出 true
console.log(1 instanceof Person); // 输出 false- 使用
Symbol.toPrimitive方法将对象转换为基本类型值:
Symbol.toPrimitive方法的第一个参数hint指示将对象转换为哪种基本类型值。hint的值可以是以下三种之一:
- "default":在字符串和数值上下文中都可以使用。
- "number":在数值上下文中使用,如运算符、Math函数等。
- "string":在字符串上下文中使用,如字符串连接运算符(+)等。
- 当 hint 为 "default" 时,JavaScript 引擎会根据上下文自动选择将对象转换为字符串或数值类型。
- 当 hint 为 "number" 时,JavaScript 引擎会优先将对象转换为数值类型,如果无法转换,则尝试将其转换为字符串类型。
- 当 hint 为 "string" 时,JavaScript 引擎会优先将对象转换为字符串类型,如果无法转换,则尝试将其转换为数值类型。
const obj = {
valueOf() {
return 42;
},
toString() {
return 'forty-two';
},
[Symbol.toPrimitive](hint) {
if (hint === 'number') {
return 42;
}
if (hint === 'string') {
return 'forty-two';
}
return 'default';
}
};
console.log(obj + 1); // 输出"default1"(因为hint为"default"时,JavaScript引擎会将对象转换为字符串类型)
console.log(obj * 2); // 输出84(因为hint为"number"时,JavaScript引擎会将对象转换为数值类型)
console.log(String(obj)); // 输出forty-two(因为hint为"string"时,JavaScript引擎会将对象转换为字符串类型)- 使用
Symbol.iterator方法迭代一个自定义对象:
/**
* 在下面的示例中,myObj 对象定义了一个自定义的迭代器,该迭代器使用 Symbol.iterator 方法返回一个迭代器对象,
* 该迭代器对象实现了 next() 方法,用于迭代 myObj 对象的 data 属性中的元素。
* 通过使用 for...of 语句,可以使用自定义的迭代器来迭代 myObj 对象中的元素。
*/
const myObj = {
data: [1, 2, 3],
[Symbol.iterator]: function() {
let index = 0;
const data = this.data;
return {
next: function() {
if (index >= data.length) {
return { done: true };
}
return { value: data[index++], done: false };
}
};
}
};
for (const item of myObj) {
console.log(item);
}迭代器 Iterator
迭代器(Iterator)是对象的一个属性(Symbol.Iterator),为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署了 Iterator 接口,就可以完成遍历操作。
- ES6 创造了一种新的遍历语句
for...of循环,Iterator 接口主要供for...of消费。 - 原生具备 Iterator 接口的数据结构如下:
- Array
- Map
- Set
- String
- TypedArray
- 函数的 arguments 对象
- NodeList 对象
- 工作原理
- 创建一个指针对象,指向当前数据结构的起始位置。遍历器对象本质上,就是一个指针对象;
- 第一次调用指针对象的
next方法,可以将指针指向数据结构的第一个成员; - 接下来不断调用指针对象的
next方法,指针一直向后移动,直到它指向最后一个成员; - 每次调用
next方法返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
注:需要自定义遍历数据的时候,可以使用
Symbol.iterator方法。
自定义迭代器:
const obj = {
data: [1, 2, 3],
[Symbol.iterator]: function() {
let index = 0;
const data = this.data;
return {
next: function() {
if (index >= data.length) {
return { done: true };
}
return { value: data[index++], done: false };
}
};
}
};
for (const v of obj) {
console.log(v);
};
// 1
// 2
// 3手动调用对象的 Symbol.iterator 方法,可以返回一个迭代器对象,该迭代器对象实现了 next() 方法,用于迭代对象中的元素:
const arr = [1, 2, 3];
const iterator = arr[Symbol.iterator]();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }生成器 Generator
生成器(Generator)是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。
生成器(Generator)是一个特殊的函数,可以用来控制函数的执行过程,手动暂停和恢复代码执行。生成器函数和普通函数不同,普通函数一旦开始执行,无法暂停,而生成器函数可以执行一段暂停后再继续执行的代码。
语法:
// `*` 号在 `function` 关键字与函数名之间,可以偏左也可以偏右,表示该函数是一个生成器函数。
function* generator() {
yield xxx;
// ...
}生成器函数的执行结果是一个生成器对象,该对象符合迭代器协议:
function * gen(){
console.log('hello generator');
}
const g = gen();
console.log(g); // Object [Generator] {}
console.log(g.next());
// hello generator
// { value: undefined, done: true }可以使用 next 或者 for...of 语句迭代生成器对象中的元素:
function* generator() {
yield 1;
yield 2;
yield 3;
}
const iterator = generator();
// 使用 next() 方法迭代生成器对象中的元素
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
// 使用 for...of 语句迭代生成器对象中的元素
for (const v of generator()) {
console.log(v);
}
// 1
// 2
// 3生成器函数的参数传递:
next方法可以带一个参数,该参数会被当作上一个yield语句的返回值。
function* gen(arg){
const r1 = yield 111;
console.log(r1);
const r2 = yield 222;
console.log(r2);
const r3 = yield 333;
console.log(r3);
}
const iterator = gen('AAA');
console.log(iterator.next()); // { value: 111, done: false }
console.log(iterator.next('BBB')); // BBB { value: 222, done: false }
console.log(iterator.next('CCC')); // CCC { value: 333, done: false }
console.log(iterator.next('DDD')); // DDD { value: undefined, done: true }解决异步编程问题:
function* gen() {
const r1 = yield new Promise((resolve, reject) => {
setTimeout(() => {
resolve('AAA');
}, 1000);
});
console.log(r1);
const r2 = yield new Promise((resolve, reject) => {
setTimeout(() => {
resolve('BBB');
}, 1000);
});
console.log(r2);
const r3 = yield new Promise((resolve, reject) => {
setTimeout(() => {
resolve('CCC');
}, 1000);
});
console.log(r3);
}
const iterator = gen();
iterator.next().value.then((data) => {
iterator.next(data).value.then((data) => {
iterator.next(data).value.then((data) => {
iterator.next(data);
});
});
});
// AAA (1s)
// BBB (2s)
// CCC (3s)解决回调地狱问题:
function* gen() {
const r1 = yield new Promise((resolve, reject) => {
setTimeout(() => {
resolve('AAA');
}, 1000);
});
console.log(r1);
const r2 = yield new Promise((resolve, reject) => {
setTimeout(() => {
resolve('BBB');
}, 1000);
});
console.log(r2);
const r3 = yield new Promise((resolve, reject) => {
setTimeout(() => {
resolve('CCC');
}, 1000);
});
console.log(r3);
}
const iterator = gen();
function next(data) {
const { value, done } = iterator.next(data);
if (done) {
return;
}
value.then(next);
}
next();
// AAA (1s)
// BBB (2s)
// CCC (3s)异步接口调用示例:
const getUser = () => setTimeout(() => {
const data = '用户数据';
iterator.next(data);
}, 1000);
const getOrder = () => setTimeout(() => {
const data = '订单数据';
iterator.next(data);
}, 1000);
const getGoods = () => setTimeout(() => {
const data = '商品数据';
iterator.next(data);
}, 1000);
function * gen() {
const userData = yield getUser();
console.log(userData);
const orderData = yield getOrder();
console.log(orderData);
const goodsData = yield getGoods();
console.log(goodsData);
}
const iterator = gen();
iterator.next();
// 用户数据 (1s)
// 订单数据 (2s)
// 商品数据 (3s)集合 Set
ES6 提供了一种新的数据结构 Set(集合),它类似于数组,但是成员的值都是唯一的,没有重复的值。
集合实现了 iterator 接口,可以使用 for...of 语句迭代集合中的元素。
集合的属性和方法:
size属性:返回集合中元素的个数。add方法:向集合中添加一个元素,返回当前集合。delete方法:从集合中删除一个元素,返回boolean值,表示是否删除成功。has方法:判断集合中是否包含某个元素,返回boolean值。clear方法:清空集合中的所有元素。
// 创建一个空集合
const set = new Set();
set.add(1);
set.add(2);
set.add(3);
set.add(4);
set.add(5);
set.add(5);
console.log(set); // Set { 1, 2, 3, 4, 5 }
// 打印集合的元素个数
console.log(set.size); // 5
// 判断集合中是否包含某个元素
console.log(set.has(3)); // true
console.log(set.has(6)); // false
// 删除集合中的某个元素
set.delete(5);
console.log(set); // Set { 1, 2, 3, 4 }
// 遍历集合
for (const v of set) {
console.log(v);
}
// 1
// 2
// 3
// 4
// 清空集合
set.clear();
console.log(set); // Set {}示例:
- 数组去重
const arr = [1, 2, 3, 4, 5, 5, 4, 3, 2, 1];
const result = [...new Set(arr)];
console.log(result); // [ 1, 2, 3, 4, 5 ]- 判断数组中是否包含重复元素
const arr = [1, 2, 3, 4, 5, 5, 4, 3, 2, 1];
const result = arr.length === [...new Set(arr)].length;
console.log(result); // false- 求两个数组的交集
const arr1 = [1, 2, 3, 4, 5];
const arr2 = [3, 4, 5, 6, 7];
const result = [...new Set(arr1)].filter((item) => new Set(arr2).has(item));
console.log(result); // [ 3, 4, 5 ]- 求两个数组的并集
const arr1 = [1, 2, 3, 4, 5];
const arr2 = [3, 4, 5, 6, 7];
const result = [...new Set([...arr1, ...arr2])];
console.log(result); // [ 1, 2, 3, 4, 5, 6, 7 ]- 求两个数组的差集
const arr1 = [1, 2, 3, 4, 5];
const arr2 = [3, 4, 5, 6, 7];
const result = [...new Set(arr1)].filter((item) => !new Set(arr2).has(item));
console.log(result); // [ 1, 2 ]字典 Map
ES6 提供了一种新的数据结构 Map(字典),它类似于对象,但是键的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
Map 也提供了 iterator 接口,可以使用 for...of 语句迭代 Map 中的元素。
Map 的属性和方法:
size属性:返回 Map 中键值对的个数。set方法:向 Map 中添加一个键值对,返回当前 Map。get方法:获取 Map 中指定键的值,如果不存在则返回undefined。has方法:判断 Map 中是否包含指定的键,返回boolean值。delete方法:从 Map 中删除指定的键值对,返回boolean值,表示是否删除成功。clear方法:清空 Map 中的所有键值对。
// 创建一个空 Map
const map = new Map()
map.set('name', 'Qiuxc')
map.set('sayHi', function(){
console.log("hi, I'm Qiuxc");
})
map.set({a: 1}, {b: 2})
console.log(map); // Map { 'name' => 'Qiuxc', 'sayHi' => [Function (anonymous)], { a: 1 } => { b: 2 } }
// 获取 Map 的大小
console.log(map.size); // 3
// 遍历 Map
for (const v of map) {
console.log(v);
}
// [ 'name', 'Qiuxc' ]
// [ 'sayHi', [Function (anonymous)] ]
// [ { a: 1 }, { b: 2 } ]
// 判断 Map 中是否包含指定的键
console.log(map.has('name')); // true
console.log(map.has('nam')); // false
// 删除 Map 中的指定键值对
map.delete('name')
console.log(map); // Map { 'sayHi' => [Function (anonymous)], { a: 1 } => { b: 2 } }
// 清空 Map
map.clear()
console.log(map); // Map {}
湫的碎碎念