Introduction to commonly used built-in Symbols
ECMAScript 6 introduced the Symbol
primitive type, along with a series of built-in Symbol
values (known as "Well-known Symbols") to extend object behavior. These built-in Symbols
serve as keys for object properties, allowing control over how objects behave in specific operations, such as iteration, type conversion, and more.
Symbol.iterator
Symbol.iterator
defines the default iterator for an object, used in scenarios like for...of
loops or the spread operator. Objects that implement this method are called iterable objects.
const myIterable = {
*[Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
}
};
console.log([...myIterable]); // [1, 2, 3]
for (const value of myIterable) {
console.log(value); // Outputs 1, 2, 3 sequentially
}
Built-in types like Array
, String
, Map
, and Set
already implement Symbol.iterator
by default. Custom classes can also make their instances iterable by implementing this method:
class Range {
constructor(start, end) {
this.start = start;
this.end = end;
}
*[Symbol.iterator]() {
for (let i = this.start; i <= this.end; i++) {
yield i;
}
}
}
const range = new Range(1, 5);
console.log([...range]); // [1, 2, 3, 4, 5]
Symbol.toStringTag
Symbol.toStringTag
customizes the string tag returned when Object.prototype.toString()
is called on an object. By default, plain objects return [object Object]
, but this property allows modifying that behavior.
const myObj = {
[Symbol.toStringTag]: 'MyCustomObject'
};
console.log(Object.prototype.toString.call(myObj)); // "[object MyCustomObject]"
Built-in types use this feature to return more meaningful tags:
console.log(Object.prototype.toString.call(new Map())); // "[object Map]"
console.log(Object.prototype.toString.call(new Set())); // "[object Set]"
Symbol.hasInstance
Symbol.hasInstance
customizes the behavior of the instanceof
operator. When executing obj instanceof Constructor
, it actually calls Constructor[Symbol.hasInstance](obj)
.
class MyArray {
static [Symbol.hasInstance](instance) {
return Array.isArray(instance);
}
}
console.log([] instanceof MyArray); // true
console.log({} instanceof MyArray); // false
Symbol.species
Symbol.species
specifies the constructor for derived objects. When creating derived objects (e.g., when Array.prototype.map()
returns a new array), it uses the constructor specified by this property instead of the default one.
class MyArray extends Array {
static get [Symbol.species]() {
return Array; // Overrides the default MyArray return
}
}
const myArr = new MyArray(1, 2, 3);
const mapped = myArr.map(x => x * 2);
console.log(mapped instanceof MyArray); // false
console.log(mapped instanceof Array); // true
Symbol.toPrimitive
Symbol.toPrimitive
allows objects to customize type conversion behavior. When an object needs to be converted to a primitive value (e.g., via the +
operator or String()
function), this method is called.
const temperature = {
value: 25,
unit: '°C',
[Symbol.toPrimitive](hint) {
switch (hint) {
case 'string':
return `${this.value}${this.unit}`;
case 'number':
return this.value;
default:
return this.value.toString();
}
}
};
console.log(String(temperature)); // "25°C"
console.log(+temperature); // 25
console.log(temperature + 5); // 30
Symbol.isConcatSpreadable
Symbol.isConcatSpreadable
determines whether an array or array-like object spreads its elements in Array.prototype.concat()
. By default, arrays spread, while array-like objects do not.
const arr1 = [1, 2];
const arr2 = [3, 4];
arr2[Symbol.isConcatSpreadable] = false;
console.log(arr1.concat(arr2)); // [1, 2, [3, 4]]
const arrayLike = { 0: 'a', 1: 'b', length: 2 };
arrayLike[Symbol.isConcatSpreadable] = true;
console.log(['x'].concat(arrayLike)); // ['x', 'a', 'b']
Symbol.match
Symbol.match
allows objects to customize the behavior of String.prototype.match()
. Regular expressions implement this method by default.
const customMatcher = {
[Symbol.match](string) {
return string.includes('hello') ? ['hello'] : null;
}
};
console.log('world hello'.match(customMatcher)); // ["hello"]
console.log('world'.match(customMatcher)); // null
Symbol.replace
Symbol.replace
defines the behavior of String.prototype.replace()
. It takes the original string and the replacement value as arguments.
const customReplacer = {
[Symbol.replace](string, replacement) {
return string.split(' ').join(replacement);
}
};
console.log('a b c'.replace(customReplacer, '-')); // "a-b-c"
Symbol.search
Symbol.search
customizes the String.prototype.search()
method. It returns the index of the match.
const customSearcher = {
[Symbol.search](string) {
return string.indexOf('ECMAScript');
}
};
console.log('Learn ECMAScript 6'.search(customSearcher)); // 6
Symbol.split
Symbol.split
controls the splitting logic of String.prototype.split()
.
const customSplitter = {
[Symbol.split](string) {
return string.toUpperCase().split('');
}
};
console.log('hello'.split(customSplitter)); // ["H", "E", "L", "L", "O"]
Symbol.unscopables
Symbol.unscopables
excludes certain properties from being bound by the with
statement. Although with
is deprecated in modern JavaScript, this feature remains.
const myObject = {
foo: 1,
bar: 2,
[Symbol.unscopables]: {
foo: true // Excludes the foo property
}
};
with (myObject) {
console.log(bar); // 2
console.log(foo); // ReferenceError: foo is not defined
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:Symbol的元编程能力
下一篇:迭代协议基本概念