它是JS
的第七种类型,属于基本类型。
Symbol
函数生成的,可以传入字符作为该值的描述。 const symbol1 = Symbol('unique');const symbol2 = Symbol('unique');console.log(symbol1 === symbol2); // false
应用举例
替代用于识别的字符串常量
--- 之前const MY_ONE = 'unique-one';if (MY_ONE === 'unique-one') { ... }虽然定义了保存值的常量,但直接使用值或再定义相同值的变量也是没有问题的。--- 现在const MY_ONE = Symbol('one');if (MY_ONE === MY_ONE) { ... }如果想使用该常量,必须直接使用该常量或其一个副本,因为 Symbol('one') 是绝对唯一的。
作为间接实现私有属性和方法的途径
只有使用 private_a / private_b 变量所指向的值才能访问到相应的属性值。const private_a = Symbol('private_a');const private_b = Symbol('private_b');function Class() { this[private_a] = 'private_a';}Class.prototype[private_b] = function() { console.log('private_b');}
作为自定义接口或不同数据集合统一接口的名称
示例:为 Array 增加并集的计算方法,使用此方法可以避免未来可能的命名冲突。const CusArrayAPIMap = { union: Symbol('cus-array-api-union')};Array[CusArrayAPIMap.union] = function() { return [...new Set([...arguments].reduce((a, b) => a.concat(b), []))];};Array[CusArrayAPIMap.union]([1, 3], [2, 3]); // [1, 3, 2]
转化规则
symbol
值不可转化成数字(作为一个唯一值无对应的数值)。
symbol
值可以转化成布尔值(不管怎样,它是有值的,所以恒为true
)。 symbol
值不可被隐式转化成字符串,但是可以显示转化(隐式即计算,拿一个唯一值计算没意义,但直接打印成字符串查看是可以的)。 Symbol() + 2; // 报错Number(Symbol()); // 报错Boolean(Symbol()); // 恒为 trueif (Symbol()) { ... } // 恒执行Symbol() + '-'; // 报错String(Symbol()); // "Symbol()"Symbol().toString(); // "Symbol()"
Symbol.for()
Symbol.for(key)
与Symbol()
一样,用来返回一个唯一值。
key
与生成的值会形成映射存储在全局的symbol
注册表中。 即是说,Symbol.for(key)
是先根据key
在注册表中寻找,有直接返回,没有创建并返回。 也即是说,在相同的全局空间中(包括不同的iframe
或service worker
)传入相同的key
取出来的是同一值。 Symbol('one') === Symbol('one'); // falseSymbol('one') === Symbol.for('one'); // falseSymbol.for('one') === Symbol.for('one'); // trueSymbol.for('one') === Symbol.for('two'); // false
相配套的还有方法Symbol.keyFor()
。
Symbol
值,去注册表中查找并返回对应的key
。 let o = Symbol.for('one');Symbol.keyFor(o); // 'one'Symbol.keyFor(Symbol('two')); // undefinedSymbol.keyFor(Symbol.for('two')); // 'two'
内置的 Symbol 值
ES6
规定了11个内置接口的统一名称。
Symbol.toStringTag
指向一个字符串,表示该对象的类型标签。 通俗的说,就是使用Object.prototype.toString.call(target)
获取到的[object Name]
中的Name
。 let obj = { [Symbol.toStringTag]: 'MyTag'};console.log( Object.prototype.toString.call(target) ); // '[object MyTag]'
Symbol.toPrimitive
指向一个方法,当该对象被转化成Number
或String
型时会被调用。 该方法会接受一个字符串参数,表示当前场合需要转化成什么类型,一共有三种模式:number
,string
和default
(转化成两者都行)。 let obj = { [Symbol.toPrimitive](type) { switch(type) { case 'number': return 3; break; case 'string': return 's'; break; case 'default': return 'd'; break; } }};console.log( Number(obj) ); // 3console.log( String(obj) ); // 's'console.log( obj + 1 ); // 'd1'
Symbol.hasInstance
指向一个方法,是instanceof
命令实际调用的方法。 [] instanceof Array; // true实际是执行下面代码。Array[Symbol.hasInstance]([]); // true可自行为自定义对象添加此接口。let OBJ = { [Symbol.hasInstance](arr) { return arr.length === 2 ? true : false; }};[1] instanceof OBJ; // false[1, 2] instanceof OBJ; // true
Symbol.iterator
指向对象的默认遍历器方法(关于遍历器可看Iterator
章节)。 数组默认的遍历器方法为:Array.prototype[Symbol.iterator]
。 Symbol.species
指向一个方法,该方法返回一个构造函数。当实例需要调用自身的构造函数时方法会被调用。 有些类库是在基类的基础上修改的,当子类使用继承的方法时,作者希望返回基类的实例,而不是子类的实例。class MyArray extends Array { static get [Symbol.species]() { return Array }}let a = new MyArray(1, 2, 3);let b = a.map(x => x);console.log(b instanceof Array); // trueconsole.log(b instanceof MyArray); // falseb 是 a 的衍生对象,或称派生对象。如果没有 [Symbol.species] ,b 原型链中第一个对象应为 MyArray 原型。但现在第一个对象为 Array 的原型,即 b 直接为 Array 的实例。
Symbol.split, Symbol.match, Symbol.search, Symbol.replace
四者都是针对字符串与正则(对象)的关系,指向一个方法,具有相同行为。let reg = /m/g;let str = 'Hi, I\'m programmer monkey';str.replace(reg, 'M');实际是执行下面的代码。reg[Symbol.replace](str, 'M');
Symbol.isConcatSpreadable
指向一个布尔值,表示该对象用于Array.prototype.concat()
时是否可以展开。 默认没有该属性,即为undefined
,等同于true
。设置为false
则不展开。 let arr = [1, 2];arr[Symbol.isConcatSpreadable] = false;[3].concat(arr); // [3, [1, 2]]
Symbol.unscopables
指向一个对象,该对象指定了使用with
关键字时,哪些属性会被with
环境排除。 let obj = { id: '123', name: 'limo', [Symbol.unscopables]: { id: true }};let id = 'id';let name = 'name';with (obj) { console.log(id); // 'id' console.log(name); // 'limo'}