5

它是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()); // 恒为 true
if (Symbol()) { ... } // 恒执行

Symbol() + '-'; // 报错
String(Symbol()); // "Symbol()"
Symbol().toString(); // "Symbol()"

Symbol.for()

Symbol.for(key)Symbol()一样,用来返回一个唯一值。
不过其传入的key与生成的值会形成映射存储在全局的symbol注册表中。
即是说,Symbol.for(key)是先根据key在注册表中寻找,有直接返回,没有创建并返回。
也即是说,在相同的全局空间中(包括不同的iframeservice worker)传入相同的key取出来的是同一值。

Symbol('one') === Symbol('one'); // false
Symbol('one') === Symbol.for('one'); // false
Symbol.for('one') === Symbol.for('one'); // true
Symbol.for('one') === Symbol.for('two'); // false

相配套的还有方法Symbol.keyFor()
它会根据传入的Symbol值,去注册表中查找并返回对应的key

let o = Symbol.for('one');

Symbol.keyFor(o); // 'one'
Symbol.keyFor(Symbol('two')); // undefined
Symbol.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
指向一个方法,当该对象被转化成NumberString型时会被调用。
该方法会接受一个字符串参数,表示当前场合需要转化成什么类型,一共有三种模式:numberstringdefault(转化成两者都行)。

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) ); // 3
console.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); // true
console.log(b instanceof MyArray); // false

b 是 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'
}

wmaker
2.9k 声望4.7k 粉丝

保持节奏。