Well-Known Symbols in JavaScript
While you can create your own custom Symbols, JavaScript also comes with built-in Symbols known as Well-Known Symbols. These symbols expose internal language behaviors and allow developers to customize or override how objects behave in certain situations.
Well-known symbols are accessed as static properties of the Symbol constructor, such as Symbol.iterator, Symbol.toPrimitive, etc.
1. Symbol.iterator
The Symbol.iterator well-known symbol specifies the default iterator for an object. Used by for...of loops and the spread operator (...).
If you want to make a custom object iterable, you can implement a method keyed by Symbol.iterator.
const myIterable = {
data: [1, 2, 3],
[Symbol.iterator]: function* () {
for (let item of this.data) {
yield item * 2;
}
}
};
for (let value of myIterable) {
console.log(value); // 2, 4, 6
}
console.log([...myIterable]); // [2, 4, 6]
2. Symbol.asyncIterator
Similar to Symbol.iterator, but it specifies the default asynchronous iterator for an object. It is used by the for await...of loop.
const myAsyncIterable = {
[Symbol.asyncIterator]: async function* () {
yield await Promise.resolve(1);
yield await Promise.resolve(2);
}
};
(async () => {
for await (let val of myAsyncIterable) {
console.log(val); // 1, 2
}
})();
3. Symbol.toPrimitive
This symbol allows you to define how an object should be converted to a primitive value (string, number, or default).
const user = {
name: "John",
age: 30,
[Symbol.toPrimitive](hint) {
if (hint === 'string') {
return this.name;
}
if (hint === 'number') {
return this.age;
}
return true;
}
};
console.log(String(user)); // "John"
console.log(+user); // 30
console.log(user + ''); // "true"
4. Symbol.toStringTag
This symbol is used by Object.prototype.toString() to determine the string description of an object.
class Validator {
get [Symbol.toStringTag]() {
return 'Validator';
}
}
const v = new Validator();
console.log(Object.prototype.toString.call(v)); // "[object Validator]"
5. Symbol.hasInstance
This symbol is a method that determines if a constructor object recognizes an object as its instance. It is called by the instanceof operator.
class EvenNumbers {
static [Symbol.hasInstance](obj) {
return typeof obj === 'number' && obj % 2 === 0;
}
}
console.log(2 instanceof EvenNumbers); // true
console.log(3 instanceof EvenNumbers); // false
Conclusion
Well-known Symbols act as extension points for the JavaScript language. By implementing these symbols on your objects, you can seamlessly integrate custom data structures with native JavaScript syntax and APIs.