Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

ES6 Symbol


May 08, 2021 ES6


Table of contents


1. Overview

ES5 object property names are 字符串 which can easily cause conflicts 属性名 names. F or example, if you use an object that someone else provides, but want to add a new method (mixin pattern) to that object, the name of the new method might conflict with the existing method. I f only there were a mechanism to ensure that the names of each property were unique, thus fundamentally preventing conflicts between property names. That's why ES6 introduced Symbol.

ES6 introduces a new raw data Symbol that represents a unique value. It is the seventh 第七 the JavaScript language, and the first six are: undefined, null, Boolean, String, Number, Object.

Symbol value is Symbol 函数 function. T his means that the property name of an object can now be of two types, one is a string that was originally available and the other is a new Symbol type. Any property name that belongs to the Symbol type is unique and can be guaranteed not to conflict with other property names.

  1. let s = Symbol();
  2. typeof s
  3. // "symbol"

In the code above, the variable s is a unique value. The result of the typeof operator, which indicates that the variable s is a Symbol data type, not other types such as strings.

Note that the symbol function cannot use the new command before, otherwise an error will be reported. T his is because the resulting Symbol is a value of the original type, not an object. T hat is, properties cannot be added because the Symbol value is not an object. Basically, it is a string-like data type.

The Symbol function can accept a string as an argument to represent a description of the Symbol instance, primarily to make it easier to distinguish when displayed in the console or converted to a string.

  1. let s1 = Symbol('foo');
  2. let s2 = Symbol('bar');
  3. s1 // Symbol(foo)
  4. s2 // Symbol(bar)
  5. s1.toString() // "Symbol(foo)"
  6. s2.toString() // "Symbol(bar)"

In the code above, s1 and s2 are two Symbol values. W ithout parameters, their output in the console is Symbol(), which is not conducive to differentiation. With parameters, they are described, and the output can tell which value it is.

If Symbol's argument is an object, the object's toString method is called, converted into a string, and then a Symbol value is generated.

  1. const obj = {
  2. toString() {
  3. return 'abc';
  4. }
  5. };
  6. const sym = Symbol(obj);
  7. sym // Symbol(abc)

Note that the parameters of the Symbol function simply represent a description of the current Symbol value, so the return value of the Symbol function of the same parameter is not equal.

  1. // 没有参数的情况
  2. let s1 = Symbol();
  3. let s2 = Symbol();
  4. s1 === s2 // false
  5. // 有参数的情况
  6. let s1 = Symbol('foo');
  7. let s2 = Symbol('foo');
  8. s1 === s2 // false

In the code above, s1 and s2 are both return values for symbol functions, and the parameters are the same, but they are not equal.

Symbol values cannot be performed with other types of values and are reported as errors.

  1. let sym = Symbol('My symbol');
  2. "your symbol is " + sym
  3. // TypeError: can't convert symbol to string
  4. `your symbol is ${sym}`
  5. // TypeError: can't convert symbol to string

However, the Symbol value can be explicitly converted to a string.

  1. let sym = Symbol('My symbol');
  2. String(sym) // 'Symbol(My symbol)'
  3. sym.toString() // 'Symbol(My symbol)'

In addition, symbol values can be converted to Boolean values, but not values.

  1. let sym = Symbol();
  2. Boolean(sym) // true
  3. !sym // false
  4. if (sym) {
  5. // ...
  6. }
  7. Number(sym) // TypeError
  8. sym + 2 // TypeError

2. Symbol.prototype.description

When you create Symbol, you can add a description.

  1. const sym = Symbol('foo');

In the code above, the description of sym is the string foo.

However, reading this description requires explicitly converting Symbol into a string, which is the following method of writing.

  1. const sym = Symbol('foo');
  2. String(sym) // "Symbol(foo)"
  3. sym.toString() // "Symbol(foo)"

The above usage is not very convenient. ES2019 provides an instance property description that returns a description of Symbol directly.

  1. const sym = Symbol('foo');
  2. sym.description // "foo"

3. Symbol as the property name

Because each Symbol value is not equal, this means that Symbol 值 be used as an 标识符 for the object's property name, guaranteeing that no properties with the same name will appear. This is useful in situations in which an object consists of multiple modules and prevents a key from being accidentally overwritten or overwritten.

  1. let mySymbol = Symbol();
  2. // 第一种写法
  3. let a = {};
  4. a[mySymbol] = 'Hello!';
  5. // 第二种写法
  6. let a = {
  7. [mySymbol]: 'Hello!'
  8. };
  9. // 第三种写法
  10. let a = {};
  11. Object.defineProperty(a, mySymbol, { value: 'Hello!' });
  12. // 以上写法都得到同样结果
  13. a[mySymbol] // "Hello!"

The above code specifies the object's property name as a Symbol value through the square bracket structure and Object.defineProperty.

Note that the Symbol value cannot be used with a point operator when it is an object property name.

  1. const mySymbol = Symbol();
  2. const a = {};
  3. a.mySymbol = 'Hello!';
  4. a[mySymbol] // undefined
  5. a['mySymbol'] // "Hello!"

In the above code, because the point operator is always followed by a string, the value that mySymbol refers to as an identification name is not read, resulting in a property name that is actually a string, not a Symbol value.

Similarly, within an object, when a property is defined with a Symbol value, the Symbol value must be placed in square brackets.

  1. let s = Symbol();
  2. let obj = {
  3. [s]: function (arg) { ... }
  4. };
  5. obj[s](123);

In the above code, if s is not placed in square brackets, the key name of the property is string s, not the Symbol value represented by s.

With enhanced object writing, the obj objects in the above code can be written more concisely.

  1. let obj = {
  2. [s](arg) { ... }
  3. };

Symbol types can also be used to define a set of constants to ensure that the values of that set of constants are not equal.

  1. const log = {};
  2. log.levels = {
  3. DEBUG: Symbol('debug'),
  4. INFO: Symbol('info'),
  5. WARN: Symbol('warn')
  6. };
  7. console.log(log.levels.DEBUG, 'debug message');
  8. console.log(log.levels.INFO, 'info message');

Here's another example.

  1. const COLOR_RED = Symbol();
  2. const COLOR_GREEN = Symbol();
  3. function getComplement(color) {
  4. switch (color) {
  5. case COLOR_RED:
  6. return COLOR_GREEN;
  7. case COLOR_GREEN:
  8. return COLOR_RED;
  9. default:
  10. throw new Error('Undefined color');
  11. }
  12. }

The biggest benefit of using symbol values for constants is that no other value can have the same value, so you can guarantee that the switch statement above will work as designed.

It is also important to note that Symbol value is the name of the 属性名 the property is still a 公开属性 not a private property.

4. Example: Eliminates magic strings

魔术字符串 is a specific string or value that appears multiple 强耦合 is strongly coupled to the code. Good-style code should try to eliminate magic strings and replace them with variables with clear meanings.

  1. function getArea(shape, options) {
  2. let area = 0;
  3. switch (shape) {
  4. case 'Triangle': // 魔术字符串
  5. area = .5 * options.width * options.height;
  6. break;
  7. /* ... more code ... */
  8. }
  9. return area;
  10. }
  11. getArea('Triangle', { width: 100, height: 100 }); // 魔术字符串

In the code above, the string Triangle is a magic string. It appears several times and forms a “强耦合” is not conducive to future modification and maintenance.

The common way to eliminate magic strings is to write it as a variable.

  1. const shapeType = {
  2. triangle: 'Triangle'
  3. };
  4. function getArea(shape, options) {
  5. let area = 0;
  6. switch (shape) {
  7. case shapeType.triangle:
  8. area = .5 * options.width * options.height;
  9. break;
  10. }
  11. return area;
  12. }
  13. getArea(shapeType.triangle, { width: 100, height: 100 });

In the code above, we write Triangle as the triangle property of the shapeType object, which eliminates strong coupling.

If you analyze it carefully, it doesn't matter which value shapeType.triangle is equal to, as long as you make sure that it doesn't conflict with the values of other shapeType properties. Therefore, it's a good place to switch to symbol values.

  1. const shapeType = {
  2. triangle: Symbol()
  3. };

In the above code, the value of shapeType.triangle is set to a Symbol, which need not be modified anywhere else.

5. Traversal of the property name

Symbol a property name, when traversing an object, the property does not appear in for... i n 、 for... In the loop, it will not be returned by Object.keys(), Object.getOwnPropertyNames(), JSON.stringify().

However, it is not a private property, and there is Object.getOwnPropertySymbols() gets all symbol property names for the specified object. The method returns an array of symbol values that are all used as property names by the current object.

  1. const obj = {};
  2. let a = Symbol('a');
  3. let b = Symbol('b');
  4. obj[a] = 'Hello';
  5. obj[b] = 'World';
  6. const objectSymbols = Object.getOwnPropertySymbols(obj);
  7. objectSymbols
  8. // [Symbol(a), Symbol(b)]

The above code is an example of the Object.getOwnPropertySymbols() method, which gets all symbol property names.

Here's another example, object.getOwnPropertySymbols() method with for... Examples of in loops, Object.getOwnPropertyNames methods for comparison.

  1. const obj = {};
  2. const foo = Symbol('foo');
  3. obj[foo] = 'bar';
  4. for (let i in obj) {
  5. console.log(i); // 无输出
  6. }
  7. Object.getOwnPropertyNames(obj) // []
  8. Object.getOwnPropertySymbols(obj) // [Symbol(foo)]

In the code above, use for... Between the in loop and the Object.getOwnPropertyNames() method do not get the Symbol key name and require the Object.getOwnPropertySymbols() method.

Another new API, the Reflect.ownKeys() method, can return key names of all types, including regular key names and Symbol key names.

  1. let obj = {
  2. [Symbol('my_key')]: 1,
  3. enum: 2,
  4. nonEnum: 3
  5. };
  6. Reflect.ownKeys(obj)
  7. // ["enum", "nonEnum", Symbol(my_key)]

Because the Symbol value is the key name, it is not traversed by the regular method. We can use this feature to define non-private methods for objects that we want to use only internally.

  1. let size = Symbol('size');
  2. class Collection {
  3. constructor() {
  4. this[size] = 0;
  5. }
  6. add(item) {
  7. this[this[size]] = item;
  8. this[size]++;
  9. }
  10. static sizeOf(instance) {
  11. return instance[size];
  12. }
  13. }
  14. let x = new Collection();
  15. Collection.sizeOf(x) // 0
  16. x.add('foo');
  17. Collection.sizeOf(x) // 1
  18. Object.keys(x) // ['0']
  19. Object.getOwnPropertyNames(x) // ['0']
  20. Object.getOwnPropertySymbols(x) // [Symbol(size)]

In the above code, the size property of object x is a Symbol value, so object.keys(x), Object.getOwnPropertyNames(x) can't get it. This creates the effect of a non-private internal approach.

6. Symbol.for(),Symbol.keyFor()

Sometimes we want to re-use the same Symbol value, Symbol.for() method can do. I t accepts a string as an argument, and then searches for symbol values with that parameter as its name. If so, the Symbol value is returned, otherwise a new Symbol value with the string's name is created and registered globally.

  1. let s1 = Symbol.for('foo');
  2. let s2 = Symbol.for('foo');
  3. s1 === s2 // true

In the above code, s1 and s2 are Both Symbol values, but they are both generated by the Symbol.for method of the same parameter, so they are actually the same value.

Symbol.for() Symbol() generate new Symbols. T he difference is that the former is registered for search in a global environment and the latter is not. S ymbol.for() does not return a value of a new Symbol type with each call, but checks whether a given key already exists and creates a new value if it does not exist. For example, if you call Symbol.for ("cat") 30 times, you will return the same Symbol value each time, but if you call Symbol ("cat") 30 times, you will return 30 different Symbol values.

  1. Symbol.for("bar") === Symbol.for("bar")
  2. // true
  3. Symbol("bar") === Symbol("bar")
  4. // false

In the above code, since symbol() does not have a registration mechanism, a different value is returned for each call.

The Symbol.keyFor() method returns the key of a registered Symbol type value.

  1. let s1 = Symbol.for("foo");
  2. Symbol.keyFor(s1) // "foo"
  3. let s2 = Symbol("foo");
  4. Symbol.keyFor(s2) // undefined

In the code above, the variable s2 belongs to an unregistered Symbol value, so undefined is returned.

Note that symbol.for() is the name registered for the Symbol value and is a global environment, whether or not it is running in the global environment.

  1. function foo() {
  2. return Symbol.for('bar');
  3. }
  4. const x = foo();
  5. const y = Symbol.for('bar');
  6. console.log(x === y); // true

In the above code, Symbol.for ('bar') runs inside the function, but the resulting Symbol value is registered in the global environment. Therefore, the second run of Symbol.for ('bar') can get this Symbol value.

This global registration feature of Symbol.for() can be taken from the same value in different iframe or service workers.

  1. iframe = document.createElement('iframe');
  2. iframe.src = String(window.location);
  3. document.body.appendChild(iframe);
  4. iframe.contentWindow.Symbol.for('foo') === Symbol.for('foo')
  5. // true

In the code above, the Symbol value generated by the iframe window is available on the main page.

7. Example: Singleton mode for modules

Singleton mode refers to calling a class that returns the same instance at any time.

For Node, a module file can be thought of as a class. How to ensure that every time this module file is executed, the same instance is returned?

It's easy to think that you can put an instance on top-level object global.

  1. // mod.js
  2. function A() {
  3. this.foo = 'hello';
  4. }
  5. if (!global._foo) {
  6. global._foo = new A();
  7. }
  8. module.exports = global._foo;

Then, load the mod .js.

  1. const a = require('./mod.js');
  2. console.log(a.foo);

In the code above, variable a loads the same instance of A at any time.

However, there is a problem with global variables global._foo can be written and any file can be modified.

  1. global._foo = { foo: 'world' };
  2. const a = require('./mod.js');
  3. console.log(a.foo);

The code above distorts the scripts .js the mods and mods.

To prevent this from happening, we can use Symbol.

  1. // mod.js
  2. const FOO_KEY = Symbol.for('foo');
  3. function A() {
  4. this.foo = 'hello';
  5. }
  6. if (!global[FOO_KEY]) {
  7. global[FOO_KEY] = new A();
  8. }
  9. module.exports = global[FOO_KEY];

In the code above, it is guaranteed that global FOO_KEY will not be inadvertently overwritten, but can still be rewritten.

  1. global[Symbol.for('foo')] = { foo: 'world' };
  2. const a = require('./mod.js');

If the key name is generated using the Symbol method, the value cannot be referenced externally and, of course, cannot be overwrite.

  1. // mod.js
  2. const FOO_KEY = Symbol('foo');
  3. // 后面代码相同 ……

The above code will cause no other script to FOO_KEY. B ut there is also the problem that if you execute this script multiple times, the FOO_KEY is different. Although Node caches the execution results of the script, in general, the same script is not executed more than once, but the user can manually clear the cache, so it is not absolutely reliable.

8. The built-in Symbol value

In addition to defining the Symbol values you use, ES6 11 Symbol values that point to methods used inside the language.

Symbol.hasInstance

The Symbol.hasInstance of the object, pointing to an internal method. T his method is called when other objects use the instanceof operator to determine if it is an instance of the object. For example, within the language, foo Instanceof Foo is actually called Foo Symbol.hasInstance.

  1. class MyClass {
  2. [Symbol.hasInstance](foo) {
  3. return foo instanceof Array;
  4. }
  5. }
  6. [1, 2, 3] instanceof new MyClass() // true

In the code above, MyClass is a class, and new MyClass() returns an instance. The Symbol.hasInstance method for the instance is automatically called when the instanceof operation is performed to determine whether the operator on the left is an instance of Array.

Here's another example.

  1. class Even {
  2. static [Symbol.hasInstance](obj) {
  3. return Number(obj) % 2 === 0;
  4. }
  5. }
  6. // 等同于
  7. const Even = {
  8. [Symbol.hasInstance](obj) {
  9. return Number(obj) % 2 === 0;
  10. }
  11. };
  12. 1 instanceof Even // false
  13. 2 instanceof Even // true
  14. 12345 instanceof Even // false

Symbol.isConcatSpreadable

The Symbol.isConcatSpreadable is equal to a Boolean value, indicating whether the object can be expanded when used for Array.prototype.concat() 布尔值

  1. let arr1 = ['c', 'd'];
  2. ['a', 'b'].concat(arr1, 'e') // ['a', 'b', 'c', 'd', 'e']
  3. arr1[Symbol.isConcatSpreadable] // undefined
  4. let arr2 = ['c', 'd'];
  5. arr2[Symbol.isConcatSpreadable] = false;
  6. ['a', 'b'].concat(arr2, 'e') // ['a', 'b', ['c','d'], 'e']

The code above states that the default behavior of the array is expandable, and Symbol.isConcatSpreadable is equal to undefined by default. This property also has an expanded effect when it is equal to true.

Array-like objects do the opposite and do not expand by default. Its Symbol.isConcatSpreadable property is set to true before it can be expanded.

  1. let obj = {length: 2, 0: 'c', 1: 'd'};
  2. ['a', 'b'].concat(obj, 'e') // ['a', 'b', obj, 'e']
  3. obj[Symbol.isConcatSpreadable] = true;
  4. ['a', 'b'].concat(obj, 'e') // ['a', 'b', 'c', 'd', 'e']

The Symbol.isConSpreadcatable property can also be defined in a class.

  1. class A1 extends Array {
  2. constructor(args) {
  3. super(args);
  4. this[Symbol.isConcatSpreadable] = true;
  5. }
  6. }
  7. class A2 extends Array {
  8. constructor(args) {
  9. super(args);
  10. }
  11. get [Symbol.isConcatSpreadable] () {
  12. return false;
  13. }
  14. }
  15. let a1 = new A1();
  16. a1[0] = 3;
  17. a1[1] = 4;
  18. let a2 = new A2();
  19. a2[0] = 5;
  20. a2[1] = 6;
  21. [1, 2].concat(a1).concat(a2)
  22. // [1, 2, 3, 4, [5, 6]]

In the above code, class A1 is expandable, class A2 is not expandable, so there are different results when using concat.

Note that symbol.isConcatSpreadable's position difference, A1 is defined on the instance, A2 is defined on the class itself, and the effect is the same.

Symbol.species

The Symbol.species the object, pointing to 构造函数 This property is used when creating derived objects.

  1. class MyArray extends Array {
  2. }
  3. const a = new MyArray(1, 2, 3);
  4. const b = a.map(x => x);
  5. const c = a.filter(x => x > 1);
  6. b instanceof MyArray // true
  7. c instanceof MyArray // true

In the above code, the child class MyArray inherits the parent class Array, a is an instance of MyArray, and b and c are derivative objects of a. You might think that both b and c are generated by calling array methods, so they should be arrays (instances of Array), but they are actually instances of MyArray.

The Symbol.species property is provided to solve this problem. Now we can set the Symbol.speciess property for MyArray.

  1. class MyArray extends Array {
  2. static get [Symbol.species]() { return Array; }
  3. }

In the above code, because the Symbol.species property is defined, the function returned by the derived object is used as a constructor when the derived object is created. T his example also shows that the Symbol.species property is defined using a get value extractor. The default Symbol.species property is equivalent to the following writing.

  1. static get [Symbol.species]() {
  2. return this;
  3. }

Now, let's look at the previous example.

  1. class MyArray extends Array {
  2. static get [Symbol.species]() { return Array; }
  3. }
  4. const a = new MyArray();
  5. const b = a.map(x => x);
  6. b instanceof MyArray // false
  7. b instanceof Array // true

In the code above, the derived object generated by a.map (x sgt; x) is not an instance of MyArray, but directly an instance of Array.

Let's look at another example.

  1. class T1 extends Promise {
  2. }
  3. class T2 extends Promise {
  4. static get [Symbol.species]() {
  5. return Promise;
  6. }
  7. }
  8. new T1(r => r()).then(v => v) instanceof T1 // true
  9. new T2(r => r()).then(v => v) instanceof T2 // false

In the code above, T2 defines the Symbol.species property, T1 does not. The result is that when a derived object is created (the then method), T1 calls its own construction method, and T2 calls Promise's construction method.

In summary, symbol.speciess is designed to call the constructor specified by the property when the instance object needs to call its own constructor again while it is running. Its main purpose is that some class libraries are modified on the basis of the base class, so when the sub-class uses the inherited method, the author may want to return an instance of the base class instead of an instance of the sub-class.

Symbol.match

The Symbol.match of the object, pointing to 函数 When str.match(myObject) it is called and the return value of the method is returned.

  1. String.prototype.match(regexp)
  2. // 等同于
  3. regexp[Symbol.match](this)
  4. class MyMatcher {
  5. [Symbol.match](string) {
  6. return 'hello world'.indexOf(string);
  7. }
  8. }
  9. 'e'.match(new MyMatcher()) // 1

Symbol.replace

The Symbol.replace of an object points to 方法 that returns a return value when it is called by the String.prototype.replace method.

  1. String.prototype.replace(searchValue, replaceValue)
  2. // 等同于
  3. searchValue[Symbol.replace](this, replaceValue)

Here's an example.

  1. const x = {};
  2. x[Symbol.replace] = (...s) => console.log(s);
  3. 'Hello'.replace(x, 'World') // ["Hello", "World"]

The Symbol.replace method receives two arguments, the first being the object that the replace method is working on, hello in the example above, and the second argument being the replaced value, and the world in the example above.

Symbol.search

The Symbol.search of an object that points 方法 a method that returns a return value when it is called by the String.prototype.search method.

  1. String.prototype.search(regexp)
  2. // 等同于
  3. regexp[Symbol.search](this)
  4. class MySearch {
  5. constructor(value) {
  6. this.value = value;
  7. }
  8. [Symbol.search](string) {
  9. return string.indexOf(this.value);
  10. }
  11. }
  12. 'foobar'.search(new MySearch('foo')) // 0

Symbol.split

The Symbol.split of an object that points 方法 method that returns a return value when it is called by the String.prototype.split method.

  1. String.prototype.split(separator, limit)
  2. // 等同于
  3. separator[Symbol.split](this, limit)

Here's an example.

  1. class MySplitter {
  2. constructor(value) {
  3. this.value = value;
  4. }
  5. [Symbol.split](string) {
  6. let index = string.indexOf(this.value);
  7. if (index === -1) {
  8. return string;
  9. }
  10. return [
  11. string.substr(0, index),
  12. string.substr(index + this.value.length)
  13. ];
  14. }
  15. }
  16. 'foobar'.split(new MySplitter('foo'))
  17. // ['', 'bar']
  18. 'foobar'.split(new MySplitter('bar'))
  19. // ['foo', '']
  20. 'foobar'.split(new MySplitter('baz'))
  21. // 'foobar'

The above method redefines the behavior of the string object's split method using the Symbol.split method.

Symbol.iterator

The Symbol.iterator the object, pointing to the 默认遍历器 method.

  1. const myIterable = {};
  2. myIterable[Symbol.iterator] = function* () {
  3. yield 1;
  4. yield 2;
  5. yield 3;
  6. };
  7. [...myIterable] // [1, 2, 3]

Object for...of you loop, the Symbol.iterator returning the object's default traverser, see Iterator and for... A chapter on the loop.

  1. class Collection {
  2. *[Symbol.iterator]() {
  3. let i = 0;
  4. while(this[i] !== undefined) {
  5. yield this[i];
  6. ++i;
  7. }
  8. }
  9. }
  10. let myCollection = new Collection();
  11. myCollection[0] = 1;
  12. myCollection[1] = 2;
  13. for(let value of myCollection) {
  14. console.log(value);
  15. }
  16. // 1
  17. // 2

Symbol.toPrimitive

The Symbol.toPrimitive of the object, pointing to 方法 When the object is converted to a value of the original type, the method is called to return the original type value for the object.

When Symbol.toPrimitive is called, it accepts a string argument that represents the pattern of the current operation, which has three modes.

  • Number: This occasion needs to be converted to a value
  • String: This occasion needs to be turned into a string
  • Default: This occasion can be converted to a value or to a string

  1. let obj = {
  2. [Symbol.toPrimitive](hint) {
  3. switch (hint) {
  4. case 'number':
  5. return 123;
  6. case 'string':
  7. return 'str';
  8. case 'default':
  9. return 'default';
  10. default:
  11. throw new Error();
  12. }
  13. }
  14. };
  15. 2 * obj // 246
  16. 3 + obj // '3default'
  17. obj == 'default' // true
  18. String(obj) // 'str'

Symbol.toStringTag

The Symbol.toStringTag of the object, pointing to 方法 W hen the Object.prototype.toString if the property exists, its return value appears in the string returned by the toString method, indicating the type of object. That is, this property can be used to customize the string that follows object in the object Object or object Array.

  1. // 例一
  2. ({[Symbol.toStringTag]: 'Foo'}.toString())
  3. // "[object Foo]"
  4. // 例二
  5. class Collection {
  6. get [Symbol.toStringTag]() {
  7. return 'xxx';
  8. }
  9. }
  10. let x = new Collection();
  11. Object.prototype.toString.call(x) // "[object xxx]"

The Symbol.toStringTag property values for the new built-in objects for ES6 are as follows.

  • JSON[Symbol.toStringTag] :'JSON'
  • Math[Symbol.toStringTag] :'Math'
  • Module Object M (Symbol.toStringTag) : 'Module'
  • ArrayBuffer.prototype[Symbol.toStringTag] :'ArrayBuffer'
  • DataView.prototype[Symbol.toStringTag] :'DataView'
  • Map.prototype[Symbol.toStringTag] :'Map'
  • Promise.prototype[Symbol.toStringTag] :'Promise'
  • Set.prototype[Symbol.toStringTag] :'Set'
  • %TypedArray.prototype (Symbol.toStringTag) : 'Uint8Array' and so on
  • WeakMap.prototype[Symbol.toStringTag] :'WeakMap'
  • WeakSet.prototype[Symbol.toStringTag] :'WeakSet'
  • %MapIteratorPrototype%[Symbol.toStringTag] :'Map Iterator'
  • %SetIteratorPrototype%[Symbol.toStringTag] :'Set Iterator'
  • %StringIteratorPrototype%[Symbol.toStringTag] :'String Iterator'
  • Symbol.prototype[Symbol.toStringTag] :'Symbol'
  • Generator.prototype[Symbol.toStringTag] :'Generator'
  • GeneratorFunction.prototype[Symbol.toStringTag] :'GeneratorFunction'

Symbol.unscopables

The Symbol.unscopables the object, pointing to 对象 The object specifies which with by the with environment when using the with keyword.

  1. Array.prototype[Symbol.unscopables]
  2. // {
  3. // copyWithin: true,
  4. // entries: true,
  5. // fill: true,
  6. // find: true,
  7. // findIndex: true,
  8. // includes: true,
  9. // keys: true
  10. // }
  11. Object.keys(Array.prototype[Symbol.unscopables])
  12. // ['copyWithin', 'entries', 'fill', 'find', 'findIndex', 'includes', 'keys']

The code above states that the array has 7 properties that are excluded by the with command.

  1. // 没有 unscopables 时
  2. class MyClass {
  3. foo() { return 1; }
  4. }
  5. var foo = function () { return 2; };
  6. with (MyClass.prototype) {
  7. foo(); // 1
  8. }
  9. // 有 unscopables 时
  10. class MyClass {
  11. foo() { return 1; }
  12. get [Symbol.unscopables]() {
  13. return { foo: true };
  14. }
  15. }
  16. var foo = function () { return 2; };
  17. with (MyClass.prototype) {
  18. foo(); // 2
  19. }

The above code does not Symbol.unscopables i.e. the variable that foo will point to the outer scope.