As a Frontend Dev, Don’t You Even Know How to Use Symbol?
Symbol is one of the primitive data types in JavaScript, it represents a unique, immutable value, usually used as the key value of object properties. Because Symbol values are unique, object properties are prevented from being accidentally overwritten or modified. The following are the methods and attributes of Symbol:
Attributes
Symbol.length
The length attribute value of the Symbol constructor is 0.
Sample code:
console.log(Symbol.length); // 0method
Symbol. for()
The Symbol.for() method will return an existing symbol value based on the given string key. If not present, a new Symbol value is created and registered in the global Symbol registry.
Sample code:
const symbol1 = Symbol.for('foo');
const symbol2 = Symbol.for('foo');
console.log(symbol1 === symbol2); // trueUsage scenario: When we need to use a globally unique Symbol value, we can use the Symbol.for() method to obtain or create the value. For example, when a certain Symbol value is shared among multiple modules, we can use Symbol.for() to ensure that the obtained Symbol value is unique.
Symbol. keyFor()
The Symbol.keyFor() method returns the key of an existing Symbol value. Returns undefined if the given Symbol value does not exist in the global Symbol registry.
Sample code:
const symbol1 = Symbol.for('foo');
const key1 = Symbol.keyFor(symbol1);
const symbol2 = Symbol('bar');
const key2 = Symbol.keyFor(symbol2);
console.log(key1); // 'foo'
console.log(key2); // undefinedUsage scenario: When we need to obtain a globally unique key of a Symbol value, we can use the Symbol.keyFor() method. However, it should be noted that only when the Symbol value is registered in the global Symbol registry, the Symbol.keyFor() method can be used to obtain its key.
Symbol()
The Symbol() function returns a new, unique Symbol value. You can use the optional parameter description to add a description for the Symbol value.
Sample code:
const symbol1 = Symbol('foo');
const symbol2 = Symbol('foo');
console.log(symbol1 === symbol2); // falseUsage scenario: When we need to use a unique Symbol value, we can use the Symbol() function to create the value. Typically, we will use Symbol values as the key values of object properties to ensure that the property cannot be accidentally overwritten or modified.
Symbol.prototype.toString()
The Symbol.prototype.toString() method returns a string representation of the Symbol value that contains the description specified when the Symbol() function was created.
Sample code:
const symbol = Symbol('foo');
console.log(symbol.toString()); // 'Symbol(foo)'Usage scenario: When we need to convert a Symbol value into a string, we can use the Symbol.prototype.toString() method.
Symbol.prototype.valueOf()
The Symbol.prototype.valueOf() method returns the Symbol value itself.
Sample code:
const symbol = Symbol('foo');
console.log(symbol.valueOf()); // Symbol(foo)Usage scenario: When we need to get a Symbol value itself, we can use the Symbol.prototype.valueOf() method.
Symbol. iterator
Symbol.iterator is a predefined Symbol value that represents the default iterator method of the object. This method returns an iterator object that can be used to iterate over all traversable properties of the object.
Sample code:
const obj = { a: 1, b: 2 };
for (const key of Object.keys(obj)) {
console.log(key);
}
// Output:
// 'a'
// 'b'
for (const key of Object.getOwnPropertyNames(obj)) {
console.log(key);
}
// Output:
// 'a'
// 'b'
for (const key of Object.getOwnPropertySymbols(obj)) {
console.log(key);
}
// Output:
// No output
obj[Symbol.iterator] = function* () {
for (const key of Object.keys(this)) {
yield key;
}
}
for (const key of obj) {
console.log(key);
}
// Output:
// 'a'
// 'b'Usage scenario: When we need to customize the iteration behavior of an object, we can achieve it by defining the Symbol.iterator property. For example, for a custom data structure, we can define its Symbol.iterator method so that it can be traversed using the for…of statement.
Symbol.hasInstance
Symbol.hasInstance is a predefined Symbol value used to define the behavior of the instanceof operator of an object. When the Symbol.hasInstance method exists in the prototype chain of an object, the object can be used by the instanceof operator.
Sample code:
class Foo {
static [Symbol.hasInstance](obj) {
return obj instanceof Array;
}
}
console.log([] instanceof Foo); // true
console.log({} instanceof Foo); // falsUsage scenario: When we need to customize the instanceof behavior of an object, we can achieve it by defining the Symbol.hasInstance method.
Symbol.isConcatSpreadable
Symbol.isConcatSpreadable is a predefined Symbol value used to define the expansion behavior of the object when using the concat() method. If an object’s Symbol.isConcatSpreadable property is false, the object will not be expanded when the concat() method is called.
Sample code:
const arr1 = [1, 2];
const arr2 = [3, 4];
const obj = { length: 2, 0: 5, 1: 6, [Symbol.isConcatSpreadable]: false };
console.log(arr1.concat(arr2)); // [1, 2, 3, 4]
console.log(arr1.concat(obj)); // [1, 2, { length: 2, 0: 5, 1: 6, [Symbol(Symbol.isConcatSpreadable)]: false }]Usage scenario: When we need to customize the expansion behavior of an object when using the concat() method, it can be achieved by defining the Symbol.isConcatSpreadable property.
Symbol.toPrimitive
Symbol.toPrimitive is a predefined Symbol value used to define the behavior of an object when it is cast. If an object defines a Symbol.toPrimitive method, that method is called when the object is converted to a primitive value.
Sample code:
const obj = {
valueOf() {
return 1;
},
[Symbol.toPrimitive](hint) {
if (hint === 'number') {
return 2;
} else if (hint === 'string') {
return 'foo';
} else {
return 'default';
}
}
};
console.log(+obj); // 2
console.log(`${obj}`); // 'foo'
console.log(obj + ''); // 'default'Usage scenario: When we need to customize the behavior of an object when it is cast, it can be achieved by defining the Symbol.toPrimitive method.
Symbol.toStringTag
Symbol.toStringTag is a predefined Symbol value, which is used to define the string returned by the object when the Object.prototype.toString() method is called. If an object defines the Symbol.toStringTag property, when the object’s toString() method is called, the string corresponding to the property will be returned.
Sample code:
class Foo {
get [Symbol.toStringTag]() {
return 'Bar';
}
}
console.log(Object.prototype.toString.call(new Foo())); // '[object Bar]'Usage scenario: When we need to customize the string returned by an object when the Object.prototype.toString() method is called, it can be achieved by defining the Symbol.toStringTag property.
Symbol.species
Symbol.species is a predefined Symbol value used to define the constructor of derived objects. If an object defines the Symbol.species property, when calling the derived method of the object (such as Array.prototype.map()), the returned new object will use the constructor specified by the property.
Sample code:
class MyArray extends Array {
static get [Symbol.species]() {
return Array;
}
}
const myArr = new MyArray(1, 2, 3);
const arr = myArr.map(x => x * 2);
console.log(arr instanceof MyArray); // false
console.log(arr instanceof Array); // trueUsage scenario: When we need to customize the constructor of a derived object, it can be achieved by defining the Symbol.species property.
Symbol.match
Symbol.match is a predefined Symbol value used to define the behavior of the object when the String.prototype.match() method is called. If an object defines the Symbol.match method, when the object’s match() method is called, the method will be called for matching.
Sample code:
class Foo {
[Symbol.match](str) {
return str.indexOf('foo') !== -1;
}
}
console.log('foobar'.match(new Foo())); // true
console.log('barbaz'.match(new Foo())); // falseUsage scenario: When we need to customize the behavior of an object when the String.prototype.match() method is called, it can be achieved by defining the Symbol.match method.
Symbol.replace
Symbol.replace is a predefined Symbol value used to define the behavior of the object when the String.prototype.replace() method is called. If an object defines the Symbol.replace method, when the object’s replace() method is called, the method will be called for replacement.
Sample code:
class Foo {
[Symbol.replace](str, replacement) {
return str.replace('foo', replacement);
}
}
console.log('foobar'.replace(new Foo(), 'baz')); // 'bazbar'
console.log('barbaz'.replace(new Foo(), 'baz')); // 'barbaz'Usage scenario: When we need to customize the behavior of an object when the String.prototype.replace() method is called, it can be achieved by defining the Symbol.replace method.
Symbol.search
Symbol.search is a predefined Symbol value used to define the behavior of the object when calling the String.prototype.search() method. If an object defines Symbol.search
class Foo {
[Symbol.search](str) {
return str.indexOf('foo');
}
}
console.log('foobar'.search(new Foo())); // 0
console.log('barbaz'.search(new Foo())); // -1Usage scenario: When we need to customize the behavior of an object when the String.prototype.search() method is called, it can be achieved by defining the Symbol.search method.
Symbol. split
Symbol.split is a predefined Symbol value used to define the behavior of the object when calling the String.prototype.split() method. If an object defines the Symbol.split method, when the object’s split() method is called, this method will be called for splitting.
Sample code:
class Foo {
[Symbol.split](str) {
return str.split(' ');
}
}
console.log('foo bar baz'.split(new Foo())); // ['foo', 'bar', 'baz']
console.log('foobarbaz'.split(new Foo())); // ['foobarbaz']Usage scenario: When we need to customize the behavior of an object when the String.prototype.split() method is called, it can be achieved by defining the Symbol.split method.
Symbol. iterator
Symbol.iterator is a predefined Symbol value used to define the behavior of the object when being traversed. If an object defines the Symbol.iterator method, you can use for…of loops, spread operators, etc. to traverse the object.
Sample code:
class Foo {
constructor() {
this.items = ['foo', 'bar', 'baz'];
}
*[Symbol.iterator]() {
for (const item of this.items) {
yield item;
}
}
}
const foo = new Foo();
for (const item of foo) {
console.log(item);
}
// 'foo'
// 'bar'
// 'baz'Usage scenario: When we need to customize the behavior of an object when it is traversed, it can be achieved by defining the Symbol.iterator method. For example, we can support the traversal of custom data structures by implementing the Symbol.iterator method.
Symbol.toPrimitive
Symbol.toPrimitive is a predefined Symbol value used to define the behavior of an object when it is cast. If an object defines the Symbol.toPrimitive method, it can be cast by calling this method.
Sample code:
const obj = {
valueOf() {
return 1;
},
[Symbol.toPrimitive](hint) {
if (hint === 'default') {
return 'default';
} else if (hint === 'number') {
return 2;
} else {
return 'foo';
}
}
};
console.log(+obj); // 2
console.log(`${obj}`); // 'foo'
console.log(obj + ''); // 'default'Usage scenario: When we need to customize the behavior of an object when it is cast, it can be achieved by defining the Symbol.toPrimitive method.
Symbol.toStringTag
Symbol.toStringTag is a predefined Symbol value, which is used to define the string returned by the object when the Object.prototype.toString() method is called. If an object defines the Symbol.toStringTag property, when the object’s toString() method is called, the string corresponding to the property will be returned.
Sample code:
class Foo {
get [Symbol.toStringTag]() {
return 'Bar';
}
}
console.log(Object.prototype.toString.call(new Foo())); // '[object Bar]'Usage scenario: When we need to customize the string returned by an object when the Object.prototype.toString() method is called, it can be achieved by defining the Symbol.toStringTag property. Doing this helps us represent the type of the object more clearly.
Symbol. unscopables
Symbol.unscopables is a predefined Symbol value used to define the behavior of the object when using the with statement. If an object defines the Symbol.unscopables property, when using the with statement, the specified properties of the object will not be bound to the environment of the with statement.
Sample code:
const obj = {
a: 1,
b: 2,
c: 3,
[Symbol.unscopables]: {
c: true
}
};
with (obj) {
console.log(a); // 1
console.log(b); // 2
console.log(c); // ReferenceError: c is not defined
}Usage scenario: Since the with statement will bring some security issues and performance issues, it is not recommended to use it in actual development. However, if you really need to use the with statement, you can prevent certain properties from being mistakenly bound to the environment of the with statement by defining the Symbol.unscopables property.
Symbol.hasInstance
Symbol.hasInstance is a predefined Symbol value used to define the behavior of the object when calling the instanceof operator. If an object defines the Symbol.hasInstance method, when the instanceof operator of the object is called, this method will be called to determine whether the target object is an instance of the object.
Sample code:
class Foo {
static [Symbol.hasInstance](obj) {
return Array.isArray(obj);
}
}
console.log([] instanceof Foo); // true
console.log({} instanceof Foo); // falseUsage scenario: When we need to customize the behavior of an object when calling the instanceof operator, it can be achieved by defining the Symbol.hasInstance method. For example, we can support the judgment of custom data types by implementing the Symbol.hasInstance method.
Summarize
Symbol is a new basic data type in ES6, which is used to represent unique values. Symbol values solve the problem of attribute name conflicts at the language level, and can be used as attribute names of objects without being accidentally overwritten. In addition, Symbol also has the following characteristics:
- Symbol values are unique, and each Symbol value is unique, even if the Symbol values are created with the same description string, they will not be equal;
- Symbol values can be used as property names of objects and will not be accidentally overwritten;
- Symbol values can be used as private properties, because Symbol properties in objects cannot be accessed outside the object;
- Symbol values can be used as constants because they are unique;
- Symbol values can be used to define advanced functions such as iterators, type conversion rules, private properties, and metaprogramming.
When using Symbol, you need to pay attention to the following points:
- Symbol values cannot be created using the new operator;
- Symbol values can be created by describing strings, but description strings are not unique identifiers of Symbol values;
- Symbol properties need to be accessed with [] when used, and the . operator cannot be used;
- Multiple Symbol properties in the same object are independent and will not affect each other.
In short, Symbol is a very useful data type with a very wide range of applications in JavaScript. Using Symbol can effectively avoid the problem of attribute name conflicts, and can provide some advanced functions for objects. Proficiency in Symbol will help us write more robust, efficient and maintainable JavaScript code.
More content at PlainEnglish.io.
Sign up for our free weekly newsletter. Follow us on Twitter, LinkedIn, YouTube, and Discord.






