Symbol类型
在es5中有五种基本类型分别是字符串,数字,布尔,null 和 undefined,在es6中引入一种新的基本类型 Symbol,表示独一无二的值。常用于模拟创建对象的私有属性。
基本用法
调用全局函数 Symbol 创建,该函数接收一个字符串作为描述参数:
let sb = Symbol('sb');
console.log(sb); // Symbol(sb)
console.log(typeof sb); // symbol
不能使用 new 调用,因为 Symbol 是基本类型,不是返回对象。
因为 Symbol 本身表示独一无二的值,所以两个相同描述的 Symbol 是不相等的:
let sb = Symbol('sb');
let sb2 = Symbol('sb');
console.log(sb === sb2); // false
对象作为描述会先调用对象的 toString 方法:
let obj = {
  toString() {
    return 'Symbol obj';
  }
};
let sb = Symbol(obj);
console.log(sb); // Symbol(Symbol obj)
不能通过运算强制转换为字符串和数字:
let sb = Symbol('sb');
let desc = sb + ''; // Cannot convert a Symbol value to a string
let num = sb + 1; // Cannot convert a Symbol value to a number
Symbol相关方法
有时候我们需要用到同一个 Symbol 来做一些处理,就要用到 Symbol.for 方法来注册一个全局的 Symbol:
let sb = Symbol.for('sb');
let sb2 = Symbol.for('sb');
let obj = {
  [sb]: '12345'
};
console.log(obj[sb]);  // 12345
console.log(sb === sb2); // true
console.log(obj[sb2]); // 12345
调用该方法会先在全局的Symbol注册表中查找有没有键为'sb'的 Symbol,如果存在则返回。不存在就先创建一个新的 Symbol ,并在全局表中注册。
Symbol.keyFor 方法返回在Symbol全局注册表中检索与该 Symbol 有关的key:
let sb = Symbol.for('sb');
console.log(Symbol.keyFor(sb)); // sb
let sb2 = Symbol('sb');
console.log(Symbol.keyFor(sb2));  // undefined
用 Symbol 作为对象的属性无法在 Object.keys 和Object.getOwnPropertyNames 方法返回,es6提供一个Object.getOwnPropertySymbols 方法来返回对象所有的 Symbol 属性。
let sb = Symbol('sb');
let obj = {
  [sb]: '12345'
};
let symbols = Object.getOwnPropertySymbols(obj);
console.log(symbols);   // [ Symbol(sb) ]
console.log(obj[symbols[0]]);  // 12345
well-know Symbol
es6提供了一些内置的 Symbol 变量,用于改变js中某些行为。比如设置构造函数的Symbol.hasInstance 可以自定义 instanceof 的行为:
function SpecialNumber() {
  //
}
Object.defineProperty(SpecialNumber, Symbol.hasInstance, {
  value: function(v) {
    return v instanceof Number && (v >= 1 && v <= 100);
  }
});
let zero = new Number(0),
  two = new Number(2);
console.log(zero instanceof SpecialNumber); // false
console.log(two instanceof SpecialNumber); // true
Symbol.toStringTag 可以改变调用 Object.prototype.toString 方法的默认行为:
function Person(name) {
  this.name = name;
}
Person.prototype[Symbol.toStringTag] = 'Person';
Person.prototype.toString = function() {
  return this.name;
};
const person = new Person('wozien');
console.log(Object.prototype.toString.call(person)); // [object Person]
console.log(person.toString());  // wozien
其他内置的Symbol及用法参考MDN文档
应用场景
作为对象的属性和方法,模拟对象的私有化:
let myKey = Symbol();
let myFunc = Symbol();
let obj = {
  [myKey]: '123',
  [myFunc]() {
    return 'bar';
  }
};
console.log(obj[myKey]);  // 123
console.log(obj[myFunc]()); // bar
定义常量,防止常量重复定义:
const COLOR_RED = Symbol('red');
const COLOR_BLUR = Symbol('blue');
const COLOR_PINK = Symbol('pink');
switch (color) {
  case COLOR_RED:
    break;
  case COLOR_BLUR:
    break;
  //
}
** 作为类型的私有属性 ** 。防止定义的冲突和外部访问:
const PASSWORD = Symbol();
class Login {
    constructor(name, password) {
        this.name = name;
        this[PASSWORD] = password;
    }
    hasPassword(pw) {
        return this[PASSWORD] === pw;
    }
}
** 自定义对象的迭代行为 ** :
let obj = {
  data: ['hello', 'world'],
  [Symbol.iterator]() {
    const self = this;
    let index = 0;
    return {
      next() {
        if (index < self.data.length) {
          return {
            value: self.data[index++]
          };
        } else {
          return { done: true };
        }
      }
    };
  }
};
for (let x of obj) {
  console.log(x);
}
// hello
// world
FE-Note