闽公网安备 35020302035485号
// 堆代码 duidaima.com
const obj = { name: 'ConardLi', age: 17 };
for (const key in obj) {
if (Object.hasOwnProperty.call(obj, key)) {
const element = obj[key];
console.log(key, element);
}
}
但是如果我们开发的是一个自定义的数据结构,for in 可能就不那么好使了,比如现在有一个音乐播放器对象,我们需要实现一个自定义的播放列表,用于存储用户添加的歌曲。这个列表可以看作一个集合,其中每个元素代表一个歌曲,包含歌曲的名称、作者、时长等属性。这时我们就可以通过定义 Symbol.iterator 方法,让这个列表可以通过 for...of 循环进行遍历:class Song {
constructor(name, artist, duration) {
this.name = name;
this.artist = artist;
this.duration = duration;
}
}
class Playlist {
constructor() {
this.songs = [];
}
addSong(song) {
this.songs.push(song);
}
[Symbol.iterator]() {
let index = 0;
const songs = this.songs;
return {
next: () => ({
value: songs[index++],
done: index > songs.length
})
}
}
}
const playlist = new Playlist();
playlist.addSong(new Song('Song 1', 'Artist 1', '3:45'));
playlist.addSong(new Song('Song 2', 'Artist 2', '4:20'));
playlist.addSong(new Song('Song 3', 'Artist 3', '5:10'));
for (const song of playlist) {
console.log(song.name);
}
// 输出:
// "Song 1"
// "Song 2"
// "Song 3"
Symbol.toStringTag
默认情况下,我们在任何一个自定义的对象上调用 toString 方法,返回值都是下面这样:class People {
constructor(name, age) {
this.name = name;
this.age = age;
}
get [Symbol.toStringTag]() {
return 'People';
}
}
const people = new People('ConardLi', 17);
console.log(people.toString()); // [object People]
Symbol.toPrimitiveclass MyDateTime {
constructor(year, month, day, hour = 0, minute = 0, second = 0) {
this._datetime = new Date(year, month - 1, day, hour, minute, second);
}
[Symbol.toPrimitive](hint) {
switch (hint) {
case 'number':
return this._datetime.getTime();
case 'string':
return this._datetime.toLocaleString();
default:
return this._datetime.toString();
}
}
}
const myDate = new MyDateTime(2023, 4, 8, 15, 30, 0);
console.log(myDate); // 输出:Sat Apr 08 2023 15:30:00 GMT+0800 (中国标准时间)
console.log(myDate + 10000); // 输出:1699950200000
console.log(`${myDate}`); // 输出:"2023/4/8 下午3:30:00"
Symbol.asyncIterator
Symbol.asyncIterator 可以用来实现一个对象的异步迭代器,它可以用于遍历异步数据流,比如异步生成器函数、异步可迭代对象等。这个特性在我们需要处理异步数据流时非常有用。举一个实际的应用场景:假设我们正在开发一个异步数据源处理器,其中包含了大量的异步数据,比如网络请求、数据库查询等。这些数据需要被逐个获取并处理,同时由于数据量非常大,一次性获取全部数据会导致内存占用过大,因此需要使用异步迭代器来逐个获取数据并进行处理:
class AsyncDataSource {
constructor(data) {
this._data = data;
}
async *[Symbol.asyncIterator]() {
for (const item of this._data) {
const result = await this._processAsyncData(item);
yield result;
}
}
async _processAsyncData(item) {
// 模拟异步处理数据的过程
return new Promise((resolve) => {
setTimeout(() => {
resolve(item.toUpperCase());
}, Math.random() * 1000);
});
}
}
async function processData() {
const dataSource = new AsyncDataSource(['a', 'b', 'c', 'd', 'e']);
for await (const data of dataSource) {
console.log(data);
}
}
processData();
Symbol.hasInstanceclass MyArray {
static [Symbol.hasInstance](instance) {
return Array.isArray(instance);
}
}
const arr = [1, 2, 3];
console.log(arr instanceof MyArray); // true
Symbol.species
class MyArray extends Array {
static get [Symbol.species]() {
return Array;
}
test(){
console.log('test');
}
}
const myArray = new MyArray(1, 2, 3);
const mappedArray = myArray.map(x => x * 2);
myArray.test();
console.log(mappedArray instanceof MyArray); // false
console.log(mappedArray instanceof Array); // true
Symbol.matchclass CustomRegExp extends RegExp {
[Symbol.match](str) {
const matches = super[Symbol.match](str);
if (matches) {
return matches.map(match => {
return `匹配到了: ${match}`;
});
}
return matches;
}
}
const regex = new CustomRegExp('hello', 'g');
const result = 'hello world'.match(regex);
console.log(result); // ["匹配到了: hello"]
Symbol.replaceconst vowels = ['a', 'e', 'i', 'o', 'u'];
const customReplace = str => {
let result = '';
for (let i = 0; i < str.length; i++) {
if (vowels.includes(str[i])) {
result += '*';
} else {
result += str[i];
}
}
return result;
};
const customString = {
[Symbol.replace]: customReplace
};
const originalString = "hello world";
const result = originalString.replace(customString, '');
console.log(result); // outputs "h*ll* w*rld"
Symbol.split// 堆代码 duidaima.com
const customSplit = str => str.split(/[\s$¥€]+/);
const customRegExp = {
[Symbol.split]: customSplit
};
const string = "100$200¥300€400 500";
console.log(string.split(customRegExp)); // outputs [ '100', '200', '300', '400', '500' ]
Symbol.unscopablesconst globalVars = {
a: 10,
b: 20,
[Symbol.unscopables]: {
b: true
}
};
with (globalVars) {
console.log(a); // 输出 10
console.log(b); // 抛出 ReferenceError
}