Symbol
今年最火的莫过于 ES6 + Babel 了。ES6 引入了一些很实用很强大的特性和一些语法。今天说的 Symbol 就是其中一个。
对于这个新特性,很多人估计很少使用它,尤其是可见性封装特别好的时候。但它也有很应用场景的。那Symbol 到底是个什么东西呢?
数据类型
首先它是 ES6 引入的一个 JS 数据类型。熟悉 JavaScript 的都知道,JavaScript 有七大数据类型。
null, undefined, number, boolean, string, Array、object
// 基本数据类型:null、undefined、数字、布尔、字符串
// 复杂数据类型: 数组、对象等
而Symbol 就是类似这些的一种数据类型。利用 typeof 运算符它的结果如下:
typeof Symbol() === 'symbol'// 为 true
现在已经定位它是什么了,那它有什么用呢?
Symbol() 用于解决属性名的冲突。比如,对于同一个对象 obj,A 对其加了属性 a, 之后B修改代码也想对其加属性 a,此时如果不知情的情况下就会产生覆盖问题。
举个应用场景,比如一个人名叫“张三”,他可能拥有两个国家甚至更多国家的国籍,因此会有多个身份 Id号。此时我们直观的做法就是加 Id 属性:
var person = {
name: "zhangsan",
id: "XXXXX", // 可能多个
age: 20
}
也会有人说,用数组就好,那每个国家的 id 怎么获取?
var person = {
name: "zhangsan",
id: ["XXXXX", 'YYYYY'] // 哪个是中国人身份证的 id ?
age: 20
}
此时若是用 Symbol 便能很好的解决问题。因为传入对象属性时,同样的Symbol不相等。看下面:
Symbol('key') === Symbol('key') // false, 因为Symbol('key')
Symbol('key') 两次的返回值是不同的,且是独一无二的值。
现在解决上面的问题。
var person = {
name: "zhangsan",
age: 20
}
// Symbol('key') 生成引用类型,独一无二的,所以 chinaId 与 americaId 不等。
var chinaId = Symbol('id');
var americaId = Symbol('id');
person[chinaId] = "chinaId";
person[americaId] = "americaId";
如下:
语法
生成一个 Symbol 很容易, 直接调用 Sysmbol() 即可,当然也可以传个参数作为描述。
var sym = Symbol();
// 参数: 只是个描述
// 只是为了在控制台显示,或者转为字符串时,比较容易区分
var sym2 = Symbol('I am just a description');
sym2.toString()
// "Symbol(I am just a description)"
另外,还可以用 Symbol.for 生成一个 Symbol
var keySym = Symbol.for('key');
只是这里有点不同于上面:
Symbol.for('key') === Symbol.for('key');// 这里为 true
这说明,两次使用 Symbol.for('key') 生成的结果一样。
Symbol.for()与Symbol()这两种写法,都会生成新的Symbol。它们的区别是,前者会在全局环境中供搜索,后者不会。Symbol.for()不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key是否已经存在,如果不存在才会新建一个值。因此这里的参数与生成的 Symbol 有关。两种方式用于不同的场景。
var keySym = Symbol.for('key');
Symbol.keyFor(keySym) // "key"
特性
独一无二
这个特性上面已经说了,每次 调用 Symbol('key') 都会产生一个独一无二的值。比如调用 Symbol('key') 10次,就会得到 10 个 Symbol 类型的对象。存取
读取和修改都很方便,只是不能用 obj.prop 的形式访问。注意,这里是修改 对象的 Symbol 类型的属性。console.log(person[chinaId]); // ok
person[chinaId] = "zhangsanId";// ok
console.log(person.chinaId); // 输出 undefined
虽然不能用 obj.prop 形式访问,但还是可以用下面三种方式的。
var obj = {};
var symProp = Symbol();
// 第一种
var obj = {
[symProp]: 'YYYY'
};
// 第二种
obj[symProp] = 'XXXX';
// 第三种写法
Object.defineProperty(obj, symProp, { value: 'ZZZZ' });
只读
对应 Symbol 类型的对象,它是只读的。对它添加属性不起作用。
Symbol("key").name = 1;
// 文档里说会 TypeError
// 不过我在 Chrome 控制台使用时发现,只是不起作用而已。
下面是个例子:
var sb = Symbol('key');
sb.name = "zw";
console.log(sb);
console.log(sb.name);
上面代码中,我们对 Symbol('key') 的 name 属性赋值, 然后访问,只得到 undefined .
遍历
我们给对象添加了 Symbol() 属性,不过它终归不是普通属性。像 for...in 、 Object.keys(obj) 、Object.getOwnPropertyNames(obj)会忽略Symbol,即属性自身不可枚举。var person = {
name: "zhangsan",
age: 20
}
var chinaId = Symbol('id');
person[chinaId] = "chinaId";
for( var prop in person) {
console.log(person[prop]);
}
Object.keys(person);
Object.getOwnPropertyNames(person);
从上图看,三种遍历确实只访问了 两个基本属性,而Symbol 属性被忽略。
那怎么遍历呢?两个方法:
Object.getOwnPropertySymbols(person) //获取Symbol属性名,但是也会忽略内置的Symbol
Reflect.ownKeys(obj) //获取所有的属性名
另外,它还有一些实用的属性和方法,有兴趣的可以自己尝试下。官网