ES6 学习笔记(Symbol)

385 阅读4分钟

Symbol 规范 MDN es6-symbol

Symbol 基本特性

  • Symbol 前不能使用 new,因为其不是构造函数,是新的原始数据类型,其它的还包括(UndefinedNullBooleanStringNumber),生成的 Symbol 是一个原始类型的值,而非对象
  • Symbol 的参数只是对当前 Symbol 值的描述,因此相同参数的 Symbol 函数返回值是不相等的 只接受字符串作为参数,传入其它类型时默认调用 toString() 转换, nullundefined 除外
  • Symbol 实例任何时候都是不相等的
  • Symbol 不能与其它类型的值运算
  • Symbol 值可转为布尔值和字符串,但是不能转为数值,怎么转?如下
  • Symbol 值作为对象属性时是公开属性
  • Symbol 值不允许隐式转换
  • Symbol 值作为属性名,不会出现在 for...infor...of,也不能被 Object.keys()Object.getOwnPropertyNames()JSON.stringify() 返回,但是可以通过 Object.getOwnPropertySymbolsReflect.ownKeys() 获取,所以 Symbol 定义的属性名适合作为私有属性名或方法名
// 类型判断
typeof Symbol; // "function"
typeof Symbol(); // "symbol"
typeof Symbol('string'); // "symbol"
// 返回值
console.log(Symbol()); // Symbol()
console.log(Symbol('string')); // Symbol(string)
// 定义
Symbol(1); // Symbol(1)
Symbol(true); // Symbol(true)
Symbol(null); // Symbol(null)
Symbol(undefined); // Symbol(undefined)
Symbol({}); // Symbol([object Object])
Symbol(String); // Symbol(function String() {native code})
Symbol(function() {}); // Symbol(function (){} )
Symbol('s').toString(); // "Symbol(s)"
Symbol(class); // Uncaught SyntaxError: Unexpected token )
new Symbol(); // Uncaught TypeError: Symbol is not constructor....
Symbol(s); // Uncaught ReferrenceError: s is not define...
// 类型转换
String(Symbol('s')); // "Symbol(s)"
Boolean(Symbol()); // true
Number(Symbol()); // Uncaught TypeError: Cannot convert a Symbol value to a number...
'' + Symbol(); // Uncaught TypeError: Cannot convert s Symbol value to a string

// 基本使用
// 作为对象属性
const obj = {};
const key = Symbol();
obj[key] = 'symbolValue';
// 等效于
const obj = {
  [key]: 'symbolValue'
};
// 等效于
Object.defineProperty(obj, key, {
  value: 'symbolValue' 
});
// Symbol 类型属性不能被常规遍历出来
obj.key; // undefined
Object.keys(obj); // []
for (let k in obj){console.log(k)} // undefined
for (let k of obj){console.log(k)} // Uncaught TypeError: obj is not iterable...
// 只能用如下方法遍历出 Symbol 类型的属性
obj[key]; // "symbolValue"
Object.getOwnPropertyDescriptors(obj); // [Symbol(): {value: "symbolValue", writable: true, enumerable: true, configurable: true}]
Object.getOwnPropertySymbols(obj); // [Symbol()]
// 返回所有属性
Reflect.ownKeys(obj); // [Symbol()]

原型属性/方法

// total 3
Object.getOwnPropertyNames(Symbol.prototype);
  • constructor
  • toString
  • valueOf

静态属性/方法

// total 17,11个内置 Symbol 值:hasInstance、isConcatSpreadable、species、match、replace、search、split、iterator、toPrimitive、toStringTag、unscopables
Object.getOwnPropertyNames(Symbol); // 以下属性或方法从 hasInstance 开始都是内置的 Symbol 值
  • length

  • name

  • prototype

  • for 静态方法, 全局定义并在不同iframe或service worker中取到同一个值, 会去搜索已定义的值,如存在则直接返回

    typeof Symbol.for; // "function"
    const s1 = Symbol('for');
    const s2 = Symbol('for');
    s1 === s2; // true
    
    iframe = document.createElement('iframe');
    iframe.src = String(window.location);
    document.body.appendChild(iframe);
    iframe.contentWindow.Symbol.for('for') === Symbol.for('foo');
    
  • keyFor 获取 Symbol.for() 参数内容

    typeof Symbol.keyFor // "function"
    const s = Symbol.for('define');
    Symbol.keyFor(s); // 'define'
    // Symbol() 没有登记机制
    const ss = Symbol('define');
    Symbol.keyFor(ss); // undefined
    
  • observable

  • hasInstance 对象使用instanceof判断是否为某构造函数的实例时,自动调用对象的 Symbol.hasInstance() 方法

    class MyClass {
      [Symbo.hasInstance](foo) {
        return foo instanceof Array;
      }
      /* 等效于上面
      static [Symbo.hasInstance](foo) {
        return foo instanceof Array;
      }
      //*/
    }
    [1] instanceof new MyClass; // true
    
  • isConcatSpreadable 属性,作用于 Array.prototype.concat 时是否可以展开,默认 undefined,取值为 true 时同效

    let arr1 = [1, 2, 3];
    [4, 5].concat(arr1, 6); // [1, 2, 3, 4, 5, 6]
    arr1[Symbo.isConcatSpreadable]; // undedined
     
    let arr2 = [1, 2, 3];
    arr2[Symbol.isConcatSpreadable] = false;
    [4, 5].concat(arr2, 6); // [1, 2, 3, [4, 5], 6]
    
    // 类数组对象默认不展开
    let arg = [length: 2, 0: 1, 1: 2];
    [3, 4].concat(obj, 5); // [3, 4, arg, 5];
    obj[Symbol.isConcatSpreadable] = true;
    [3, 4].concat(obj, 5); // [3, 4, arg, 5]; // [3, 4, 1,  2, 5];
    
    class A1 extends Array {
      constructor(args) {
        super(args);
        this[Symbol.isConcatSpreadable] = true; // 定义在实例上
      }
    }
    class A2 extends Array {
      constructor(args) {
        super(args);
      }
      get [Symbol.isConcatSpreadable]() { // 定义在类本身
        return true;
      }
    }
    let a1 = new A1();
    a1[0] = 3;
    a1[1] = 4;
    let a2 = new A2();
    a2[0] = 5;
    a2[1] = 6;
    [1, 2].concat(a1).concat(a2); // [1, 2, 3, 4, 5, 6]
    
  • species 属性,指向当前对象的构造函数

    class MyArray extends Array {
      // 覆盖默认的构造函数
      static [Symbol.species]() {
        return Array;
      }
    }
    let a = new MyArray(1,2,3);
    let mapped = a.map(i => i * i);
    mapped instanceof Array; // true
    mapped instanceof MyArray; // false
    
  • match 属性,执行 str.match(myObject) 则调用

    
    
  • replace 属性,执行 String.prototype.replace 则调用

    
    
  • search 属性,执行 String.prototype.search 则调用

    
    
  • split 属性,执行 String.prototype.split 则调用

    
    
  • iterator 对象的 Symbol.iterator 属性,指向该对象的默认遍历器方法,对象进行 for...of 循环时,会调用该方法

    
    
  • toPrimitive 属性,隐式类型转换,共有3中模式: Number 需要转成数值、String 需要转成字符串、Default 该场合可以转成字符串或数值

    let obj = {
      [Symbol.toPrimitive](hint) {
        switch(hint) {
          case 'number':
            return 123;
          case 'string':
            return 'str';
          case 'default':
            return 'default';
          default:
            throw new Error();
        }
      }
    }
    2 * obj // 246
    3 + obj // '2default'
    obj === 'default'; // true
    String(obj); // 'str'
    
  • toStringTag 属性 在对象上调用 Object.prototype.toString 方法时触发,用来定制扩展 [object type]

    ({[Symbol.toStringTag: 'Foo'}.toString()) // "[object Foo]"
    class Collection {
      get [Symbol.toStringTag]() {
        return 'XXX';
      }
    }
    let x = new Collection();
    x.toString(); // "[object xxx]"
    Object.prototype.toString.call(x) // "[object xxx]"
    // 
    JSON[Symbol.toStringTag]; // "JSON"
    Math[Symbol.toStringTag]; // "Math"
    Module对象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"等
    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"
    
  • unscopables

    
    
const symbol = Symbol('descriptor');
typeof symbol === 'symbol'; // true
symbol; // Symbol(descriptor)
symbol.toString(); // "Symbol(descriptor)"

const obj = {
  toString() {
    return 'toString';
  }
}
const sym = Symbol(obj); // Symbol(toString) ,参数为对象时,默认调用对象的 toString 方法转为字符串
'string' + sym; // TypeError: can't convert symbol to sting
String(sym); // 'Symbol(toString)'
sym.toString(); // 'Symbol(toString)'
Boolean(sym); // true
!sym; // false
Number(sym);// TypeError
sym + 2; // TypeError

const obj = {
  [Symbol('key')]: 'symbolValue',
  key: 'value'
};
Reflect.ownKeys(obj); // ['key', Symbol(key)]

参考资料

Symbol Polyfill 填坑之旅