Symbol

2018-01-06  本文已影响0人  第八共同体

注:内容来自阮一峰老师的书 ECMAScript 6 入门

1.Symbol 值通过Symbol函数生成。

这就是说,对象的属性名现在可以有两种类型,

2.作为属性名的 Symbol

let mySymbol = Symbol();

// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';

// 第二种写法
let a = {
  [mySymbol]: 'Hello!'
};

// 第三种写法
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });

// 以上写法都得到同样结果
a[mySymbol] // "Hello!"

注意,Symbol 值作为对象属性名时,不能用点运算符。同理,在对象的内部,使用 Symbol 值定义属性时,Symbol 值必须放在方括号之中。

3.属性名的遍历

Symbol 作为属性名,该属性不会出现在for...in、for...of循环中,也不会被Object.keys(),Object.getOwnPropertyNames()、JSON.stringify()返回。但是,它也不是私有属性,有一个Object.getOwnPropertySymbols方法,可以获取指定对象的所有 Symbol 属性名。
另一个新的 API,Reflect.ownKeys方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。

4. Symbol.for(),Symbol.keyFor()

Symbol.for()与Symbol()这两种写法,都会生成新的 Symbol。它们的区别是,前者会被登记在全局环境中供搜索,后者不会。Symbol.for()不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key是否已经存在,如果不存在才会新建一个值。
Symbol.keyFor方法返回一个已登记的 Symbol 类型值的key。

5.模块的 Singleton 模式

Singleton 模式指的是调用一个类,任何时候返回的都是同一个实例。

6.内置的 Symbol 值

对象的Symbol.hasInstance属性,指向一个内部方法。当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法

class MyClass {
  [Symbol.hasInstance](foo) {
    return foo instanceof Array;
  }
}

[1, 2, 3] instanceof new MyClass() // true

对象的Symbol.isConcatSpreadable属性等于一个布尔值,表示该对象用于Array.prototype.concat()时,是否可以展开

let arr1 = ['c', 'd'];
['a', 'b'].concat(arr1, 'e') // ['a', 'b', 'c', 'd', 'e']
arr1[Symbol.isConcatSpreadable] // undefined

let arr2 = ['c', 'd'];
arr2[Symbol.isConcatSpreadable] = false;
['a', 'b'].concat(arr2, 'e') // ['a', 'b', ['c','d'], 'e']

对象的Symbol.species属性,指向一个构造函数。创建造衍生对象时,会使用该属性.

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

const a = new MyArray();
a.map(x => x) instanceof MyArray // false
a.map(x => x) instanceof Array // true

其中,默认的Symbol.species属性等同于下面的写法。

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

对象的Symbol.match属性,指向一个函数。当执行str.match(myObject)时,如果该属性存在,会调用它,返回该方法的返回值。

class MyMatcher {
  [Symbol.match](string) {
    return 'hello world'.indexOf(string);
  }
}

'e'.match(new MyMatcher()) // 1

对象的Symbol.replace属性,指向一个方法,当该对象被String.prototype.replace方法调用时,会返回该方法的返回值。

const x = {};
x[Symbol.replace] = (...s) => console.log(s);

'Hello'.replace(x, 'World') // ["Hello", "World"]
class MySearch {
  constructor(value) {
    this.value = value;
  }
  [Symbol.search](string) {
    return string.indexOf(this.value);
  }
}
'foobar'.search(new MySearch('foo')) // 0
class MySplitter {
  constructor(value) {
    this.value = value;
  }
  [Symbol.split](string) {
    let index = string.indexOf(this.value);
    if (index === -1) {
      return string;
    }
    return [
      string.substr(0, index),
      string.substr(index + this.value.length)
    ];
  }
}

'foobar'.split(new MySplitter('foo'))
// ['', 'bar']

对象的Symbol.iterator属性,指向该对象的默认遍历器方法。

class Collection {
  *[Symbol.iterator]() {
    let i = 0;
    while(this[i] !== undefined) {
      yield this[i];
      ++i;
    }
  }
}

let myCollection = new Collection();
myCollection[0] = 1;
myCollection[1] = 2;

for(let value of myCollection) {
  console.log(value);
}
// 1
// 2
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 // '3default'
obj == 'default' // true
String(obj) // 'str'
class Collection {
  get [Symbol.toStringTag]() {
    return 'xxx';
  }
}
let x = new Collection();
Object.prototype.toString.call(x) // "[object xxx]"

对象的Symbol.unscopables属性,指向一个对象。该对象指定了使用with关键字时,哪些属性会被with环境排除。

// 没有 unscopables 时
class MyClass {
  foo() { return 1; }
}

var foo = function () { return 2; };

with (MyClass.prototype) {
  foo(); // 1
}

// 有 unscopables 时
class MyClass {
  foo() { return 1; }
  get [Symbol.unscopables]() {
    return { foo: true };
  }
}

var foo = function () { return 2; };

with (MyClass.prototype) {
  foo(); // 2
}
上一篇 下一篇

猜你喜欢

热点阅读