Today, let's learn the new primitive type Symbol of JavaScript. We can use Symbol to create a unique value as an object attribute or value, or modify the internal logic of JS language through Symbol's well-known.
Create Symbol
ES6 adds symbol as the original data type, and other original data types, such as number, boolean, null, undefined and string. The symbol type has no text form.
To create a symbol, we use the global function Symbol()
let s = Symbol('foo');
Each call to the Symbol() function creates a new unique value
console.log(Symbol() === Symbol()); // false
The Symbol() function accepts an optional parameter as a description, which makes the Symbol more semantic.
The following two symbol s are created: firstName and lastName
let firstName = Symbol('first name'), lastName = Symbol('last name');
When we use console.log() to print symbol, we will implicitly call the toString() method of symbol.
console.log(firstName); // Symbol(first name) console.log(lastName); // Symbol(last name)
Since symbol is the original value, we can use typeof to check its type. Similarly, ES6 expands the typeof keyword and returns symbol when encountering symbol type
console.log(typeof firstName); // symbol
Because it is an original type, you cannot use new to create it
let s = new Symbol(); // error
Share symbol
To create a shared symbol, use the Symbol.for() function instead of Symbol().
Symbol.for() also accepts an optional parameter as a description
let ssn = Symbol.for('ssn');
Symbol.for() will first find out whether there is a symbol of the created ssn in the global. If so, it will return the created symbol. If not, it will create a new symbol.
Next, let's create the same symbol and see if it's not the same symbol
let nz = Symbol.for('ssn'); console.log(ssn === nz); // true
Because the symbol of ssn has been created above, the symbol of nz variable will be the same as that created above.
If you want to get the key of symbol, use the Symbol.keyFor() method
console.log(Symbol.keyFor(nz)); // 'ssn'
Note that if the symbol is created through Symbol(), using Symbol.keyFor() returns undefined
let systemID = Symbol('sys'); console.log(Symbol.keyFor(systemID)); // undefined
What's the use of symbols
1) Use Symbol as unique value
We often use strings or numbers to represent some states in the code, and we often face the problem of lack of semantics or repeated definition. At this time, using symbols is the best choice. Each newly created Symbol is unique and will not produce repetition, and we can pass in corresponding descriptions to symbols.
Looking at the following example, we use symbols to express several statuses of orders instead of strings and numbers
let statuses = { OPEN: Symbol('Order placed'), IN_PROGRESS: Symbol('In distribution'), COMPLETED: Symbol('Order completion'), CANCELED: Symbol('Order cancellation') }; // Complete order task.setStatus(statuses.COMPLETED);
2) Use symbol as object attribute
Use Symbol as the attribute name
let status = Symbol('status'); let task = { [status]: statuses.OPEN, description: 'study ES6 Symbol' }; console.log(task);
Use Object.keys() to get all enumerable properties of the object
console.log(Object.keys(task)); // ["description"]
Use Object.getOwnPropertyNames() to get all properties, whether enumerable or not
console.log(Object.getOwnPropertyNames(task)); // ["description"]
To get the Symbol property in the object, you need to use the Object.getOwnPropertySymbols() method added in ES6
console.log(Object.getOwnPropertySymbols(task)); //[Symbol(status)]
Well-known symbol
ES6 defines Symbol related attributes on the prototype chain to expose more language internal logic. Well known Symbol defines some functions for standard objects that were previously only visible within the language.
Symbol.hasInstance
Symbol.hasInstance is a symbol that changes the default behavior of the instanceof operator. We usually use instanceof in this way
obj instanceof type;
Then JavaScript will execute the symbol. Hastance method, as follows
type[Symbol.hasInstance](obj);
It will call the Symbol.hasInstance static method of type with obj as the parameter
class Stack { } console.log([] instanceof Stack); // false
[] array is not an instance created by Stack class, so false is returned.
Assuming that the [] array is an instance created by the Stack class and returns true, we can override the method of Symbol.hasInstance
class Stack { static [Symbol.hasInstance](obj) { return Array.isArray(obj); } } console.log([] instanceof Stack); // true
Symbol.iterator
Symbol.iterator specifies whether the function returns an iterator of the object.
Objects with the Symbol.iterator attribute are called iteratable objects.
In ES6, Array, Set, Map, and string are all iteratable objects.
ES6 provides a for... of loop, which can be used on iteratable objects.
var numbers = [1, 2, 3]; for (let num of numbers) { console.log(num); } // 1 // 2 // 3
In the back, the JavaScript engine first calls the Symbol.iterator method of the numbers array to obtain the iterator object, and then it calls the iterator.next() method and copies the value attribute of the iterator object into the num variable. After three iterations, the done attribute of the object is true and is pushed out circularly.
We can get the iterator object of the array through Symbol.iterator.
var iterator = numbers[Symbol.iterator](); console.log(iterator.next()); // Object {value: 1, done: false} console.log(iterator.next()); // Object {value: 2, done: false} console.log(iterator.next()); // Object {value: 3, done: false} console.log(iterator.next()); // Object {value: undefined, done: true}
By default, a self-defined collection cannot be iterated, but we can make it iteratable with Symbol.iterator
class List { constructor() { this.elements = []; } add(element) { this.elements.push(element); return this; } *[Symbol.iterator]() { for (let element of this.elements) { yield element; } } } let chars = new List(); chars.add('A') .add('B') .add('C'); // Iteration is implemented using Symbol.iterator for (let c of chars) { console.log(c); } // A // B // C
Symbol.isConcatSpreadable
We can use the concat() method to combine two arrays
let odd = [1, 3], even = [2, 4]; let all = odd.concat(even); console.log(all); // [1, 3, 2, 4]
We can also use concat() to pass in a single element instead of an array
let extras = all.concat(5); console.log(extras); // [1, 3, 2, 4, 5]
In the above example, when we pass an array to the concat() method, the concat() method will expand the array into a single element. However, it will treat a single original parameter in a different way, and we can't change this behavior until ES6.
let list = { 0: 'JavaScript', 1: 'Symbol', length: 2 }; let message = ['Learning'].concat(list); console.log(message); // ["Learning", Object]
The list object is merged into the ['Learning '] array, but the elements in the list object are not merged into the array.
To add the elements in the list object to the array separately during concat(), we need to add the symbol.isconcapspreable attribute to the list object, as follows
let list = { 0: 'JavaScript', 1: 'Symbol', length: 2, [Symbol.isConcatSpreadable]: true }; let message = ['Learning'].concat(list); console.log(message); // ["Learning", "JavaScript", "Symbol"]
If Symbol.isConcatSpreadable is set to false, concat() will merge the entire list object into the array.
Symbol.toPrimitive
The Symbol.toPrimitive method determines the behavior when an object is converted to its original value.
The JavaScript engine defines the Symbol.toPrimitive method on the prototype of each type value.
The Symbol.toPrimitive method accepts a hint parameter, which will be the following three values: string, number and default. The hint parameter is used to specify the type of return value. The hint parameter is populated by the JavaScript engine based on the context of the object being used.
function Money(amount, currency) { this.amount = amount; this.currency = currency; } Money.prototype[Symbol.toPrimitive] = function(hint) { var result; switch (hint) { case 'string': result = this.amount + this.currency; break; case 'number': result = this.amount; break; case 'default': result = this.amount + this.currency; break; } return result; } var price = new Money(10000, 'RMB'); console.log('I have ' + price); // Price is 799USD console.log(+price + 1); // 800 console.log(String(price)); // 799USD
other
-
Symbol.match(regex): a method called when calling the String.prototype.match() method to compare strings.
-
Symbol.replace(regex, replacement): a method called when calling the String.prototype.replace() method, which is used to replace the substring of the string.
-
Symbol.search(regex): a method called when calling the String.prototype.search() method to locate a substring in a string.
-
Symbol. Categories (regex): constructor used to create derived objects.
-
Symbol.split: a method called when calling the String.prototype.split() method, which is used to split the string.
-
Symbol.toStringTag: a string used when calling the String.prototype.toString() method to create an object description.
-
Symbol.unscopables: a collection of objects that define object attribute names that cannot be referenced by the with statement.
summary
Today, we learned all the usage methods of Symbol and some commonly used methods. I hope it can help you.
If this article is helpful, wechat search [Xiaoshuai's Programming Notes] and let us make progress every day