关于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内置的属性比较多,下次有机会再介绍