在 ECMAScript 6 中引入了新的原生数据类型 Symbol,它的出现使得开发者可以创建独一无二的值,从而避免命名冲突和属性覆盖等问题。而在 ECMAScript 2021 中,Symbol 又有了更多的应用场景,同时还提供了一个新的内置的迭代器方法 Symbol.iterator,使得开发者可以更加便捷地使用迭代器。
Symbol
Symbol 是一种原始数据类型,用来表示一个独一无二的值。我们可以使用以下代码来创建一个 Symbol:
const mySymbol = Symbol();
其中,Symbol 函数的参数是可选的,如果传入参数,就相当于一个 Symbol 实例的描述,但本质上它们是一样的。
1. Symbol 的应用场景
1.1. 属性名的保护
在对象中使用 Symbol 作为属性名,可以保证该属性名不被意外覆盖或访问到。如下面例子所示:
const x = Symbol();
const obj = {
[x]: 'Hello',
y: 'World'
};
console.log(obj[x]); // 'Hello'
console.log(obj.y); // 'World'在这个例子中,我们将 x 设为对象 obj 的一个属性名,并将其值设为 'Hello'。而 y 属性则没有使用 Symbol,可以正常访问。
1.2. 私有属性和方法
使用 Symbol 来定义类的私有属性和方法,从而保证不会被外部访问到。如下所示:
// www.javascriptcn.com code example
const _name = Symbol('name');
class MyClass {
constructor(name) {
this[_name] = name;
}
[_name]() {
console.log(`My name is ${this[_name]}`);
}
}
const c = new MyClass('Tom');
c[_name](); // TypeError: c[_name] is not a function
在这个例子中,我们将 _name 设为私有属性的键名。并且在类的构造函数中初始化,并使用 [_name] 来定义私有方法。由此,在类外部将会不能访问到这些私有属性和方法。
1.3. 定义枚举
使用 Symbol 枚举,从而保证枚举值的独一无二性。如下所示:
// www.javascriptcn.com code example
const Role = {
admin: Symbol('admin'),
user: Symbol('user'),
guest: Symbol('guest')
};
console.log(Role.admin); // Symbol(admin)
console.log(Role.user); // Symbol(user)
console.log(Role.guest); // Symbol(guest)在这个例子中,我们使用 Symbol 作为 Role 对象的属性名,从而定义了三个具有独一无二性的枚举值。
2. Symbol 属性的遍历
使用 Object.getOwnPropertySymbols 方法可以遍历出对象中所有使用 Symbol 作为属性名的属性。如下所示:
// www.javascriptcn.com code example
const mySymbol1 = Symbol();
const mySymbol2 = Symbol();
const obj = {
[mySymbol1]: 'Hello',
[mySymbol2]: 'World',
x: 'x'
};
const symbols = Object.getOwnPropertySymbols(obj);
console.log(symbols); // [ Symbol(), Symbol() ]
for (let s of symbols) {
console.log(obj[s]); // 'Hello' 'World'
}在这个例子中,使用 Object.getOwnPropertySymbols 方法遍历出 obj 对象中所有使用 Symbol 作为属性名的属性,并将它们存放在数组中。然后借助 for...of 循环输出属性名对应的值。
Symbol.iterator
Symbol.iterator 是 ECMAScript 6 新增的内置迭代器,它是一种通用的遍历方法,可以被所有的 Iterable 对象使用。使用 Symbol.iterator 可以方便地遍历出数据结构中的所有元素。
1. Iterator 接口实现
在一个对象上实现 Symbol.iterator 接口,需要满足以下两个条件:
- 对象需要具有一个 next 方法,返回一个满足 { value, done } 格式的对象。
- 对象上需要具有 Symbol.iterator 属性,该属性值为一个返回 Iterator 对象的函数。
以实现数组遍历为例:
// www.javascriptcn.com code example
const arr = [1, 2, 3];
arr[Symbol.iterator] = function() {
let index = 0;
return {
next: () => {
return {
value: this[index],
done: index++ >= this.length
};
}
};
}
for (let i of arr) {
console.log(i); // 1 2 3
}在这个例子中,我们自己实现了 arr 数组上的 Symbol.iterator 方法。返回一个对象,该对象具有一个 next 方法。通过依次迭代数组中的元素,返回一个满足 { value, done } 的对象。
2. 内置迭代器
除了用户可以自己实现 Symbol.iterator 迭代器,许多内置容器对象也已经内置了该接口,方便我们进行遍历。
2.1. 数组的迭代器
数组已经内置了 Symbol.iterator 方法,可以使用 for...of 进行遍历。
const arr = [1, 2, 3];
for (let i of arr) {
console.log(i); // 1 2 3
}2.2. 字符串的迭代器
字符串也内置了 Symbol.iterator 方法,可以使用 for...of 进行遍历。
const str = 'hello';
for (let s of str) {
console.log(s); // 'h' 'e' 'l' 'l' 'o'
}2.3. Map 和 Set 迭代器
Map 和 Set 对象本身也内置了 Symbol.iterator 方法,可以使用 for...of 进行遍历。
// www.javascriptcn.com code example
const map = new Map();
map.set('a', 1).set('b', 2);
for (let [key, val] of map) {
console.log(`key: ${key}, value: ${val}`); // 'key: a, value: 1' 'key: b, value: 2'
}
const set = new Set();
set.add(1).add(2).add(3);
for (let val of set) {
console.log(val); // 1 2 3
}3. 可迭代对象
具有 Symbol.iterator 属性的对象被称为可迭代对象,可以被 for...of 循环进行遍历。常见的可迭代对象包括数组、字符串、Set、Map。
结语
在 ECMAScript 2021 中,Symbol 和 Symbol.iterator 的应用更加广泛,对于开发者而言,掌握它们的应用和用法非常有必要。希望这篇文章对你有所帮助。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67d7b9b1a941bf7134dd9ea4