// 堆代码 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.toPrimitive
class 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.hasInstance
class MyArray { static [Symbol.hasInstance](instance) { return Array.isArray(instance); } } const arr = [1, 2, 3]; console.log(arr instanceof MyArray); // trueSymbol.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); // trueSymbol.match
class 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.replace
const 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.unscopables
const globalVars = { a: 10, b: 20, [Symbol.unscopables]: { b: true } }; with (globalVars) { console.log(a); // 输出 10 console.log(b); // 抛出 ReferenceError }