关于Symbol你知道多少?

关于Symbol你知道多少?

Symbol是JS数据类型的第七种,听过undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object),但是Symbol是不是有点陌生?symbol的出现优雅的解决了一些基本问题,比如大型项目对人合作时容易出现引用混乱的问题。

1、如何生成Symbol

Symbol 值通过Symbol函数生成:

let symbl = Symbol();
typeof symbl // "symbol"

通过上面的形式很容易得出:Symbol的生成没有关键字new,这意味着它是一种原始数据类型。
实际上是一种类似于字符串的数据类型。既不是对象也不是数组
当你在声明的时候出入了参数:

let symbl = Symbol('hello');
symbl.description // "hello"
//Symbol(hello)

参数的意义在于让你更容易区别当前的值。
实际上Symbol函数的参数只是表示对当前 Symbol 值的描述

let s1 = Symbol('2');
let s2 = Symbol('2');
s1 === s2 // false

即使没有描述,两个值也是不相等的。这和对象有些类似
重点:

每一个Symbol都是独立的惟一的。

2、Symbol使用与特性

1、Symbol 值不能与其他类型的值进行运算

 let sym = Symbol('1');
"2" + sym
// TypeError: can't convert symbol to string

but 可以转换为布尔值:

 let sym = Symbol('1');
 Boolean(sym) // true

2、如何防止引用混乱

由于Symbol天然的特性,使得当其作为属性的key值时,可以对当前键值对进行防篡改。

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

but此时用.运算符会失效:

a.mySymbol;//undefined

所以在对象内部声明一个symbol类型时,要用[];

3、不会被常规的循环,遍历

不会遍历symbol类型的循环方法有:
for…in、for…of、Object.keys()、Object.getOwnPropertyNames()。
但是:**Reflect.ownKeys()**例外:

let obj = {
  [Symbol('key')]: 1,
   name:'le'
};

Reflect.ownKeys(obj)
//  ["name", Symbol(key)]

4、Symbol.for() 与 Symbol.keyFor()

以上两种方法看起来似乎与遍历相关,其实没有一点关系
1、Symbol.for()
调用方法会返回一个Symbol值:

  let s1 = Symbol.for('foo'); // Symbol(foo)

那这样就和Symbol()没有区别了。

区别:Symbol.for()不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key是否已经存在,如果不存在才会新建一个值

let s1 = Symbol.for('foo');
let s2 = Symbol.for('foo');
s1 === s2 // true

这意味着Symbol.for()的唯一ID就是参数。
2、Symbol.keyFor()

let s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"
let s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined

很明显,Symbol.keyFor()是为Symbol.for()服务的。返回一个已登记的 Symbol 类型值的key

function foo() {
  return Symbol.for('bar');
}
const x = foo();
const y = Symbol.for('bar');
console.log(x === y); // true

注意:Symbol.for()为 Symbol 值登记的名字,是全局环境的,不管有没有在全局环境运行。

3、几种内置属性

原本打算把这个小段放在上面的,但是这个确实很重要!Symbol上的属性其实是可以对目标对象的一些操作进行改写,这种级别的修改应该可以算得上元编程的范围了。

1、Symbol.iterator

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

这个属性的重要在于,es6规定,默认的iterator接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是可以遍历的。

var str = new String("hi");
[...str] // ["h", "i"]
str[Symbol.iterator] = function() {
  return {
    next: function() {
      if (this._first) {
        this._first = false;
        return { value: "bye", done: false };
      } else {
        return { done: true };
      }
    },
    _first: true
  };
};
[...str] // ["bye"]
str // "hi"

其实这和生成器generator 有很大关系,可以看这里

2、Symbol.hasInstance

class MyClass {
  [Symbol.hasInstance](foo) {
    return foo instanceof Array;
  }
}
[1, 2, 3] instanceof new MyClass() // true

上面代码中,MyClass是一个类,new MyClass()会返回一个实例。该实例的Symbol.hasInstance方法,会在进行instanceof运算时自动调用,判断左侧的运算子是否为Array的实例。

3、Symbol.replace

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

'Hello'.replace(x, 'World') // ["Hello", "World"]

上面replace方法生效时,会执行修改后的函数.

当然,Symbol内置的属性比较多,下次有机会再介绍

上一篇:c++11 thread(初步)


下一篇:[POI2013]LUK-Triumphal arch