一, 概述
ES5 的对象属性名都是字符串, 这容易造成属性名冲突. ES6 引入了一种新的原始数据类型 Symbol, 表示独一无二的值. 它是 JavaScript 语言的第七种数据类型. 前六种是: undefined,null,Boolean,String,Number,Object.
Symbol 值通过 Symbol 函数生成. 这就是说, 对象的属性名现在有两种类型, 一种是字符串, 一种是新增的 Symbol 类型. 凡是属性名属于 Symbol 类型, 就是独一无二的, 可以保证不会与其他属性名冲突.
1. 基本用法
- let s = Symbol();
- typeof s;//"symbol"
注: Symbol 函数前不能使用 new 命令, 否则会报错. 这是因为生成的 Symbol 是一个原始类型的值, 不是对象, 即也不能添加属性.
Symbol 函数可以接受一个字符串作为参数, 表示对 Symbol 实例的描述, 主要是为了在控制台显示, 或者转为字符串时比较容易区分.
- var s1 = Symbol();
- var s2 = Symbol();
- s1;//Symbol()
- s2;//Symbol()
- var s3 = Symbol('foo');
- var s4 = Symbol('bar');
- s3;//Symbol(foo)
- s4;//Symbol(bar)
不加参数, 打印出来都是 "Symbol()" , 不利于区分, 加上参数, 等于有了描述, 输出的时候就可以区分是哪一个的值.
2. 注意问题
(1)Symbol 函数的参数只是表示对当前 Symbol 值的描述, 因此相同参数的 Symbol 函数的返回值是不相等.
- // 无参数的情况
- let s1 = Symbol();
- let s2 = Symbol();
- s1 === s2;//false
- // 有参数的情况
- var s1 = Symbol('foo');
- var s2 = Symbol('foo');
- s1 === s2;//false
(2)Symbol 值不能与其他类型的值进行运算, 会报错.
- var s1 = Symbol('foo');
- s1 + 'ss';//TypeError: Cannot convert a Symbol value to a string
- s1 + 1;//TypeError: Cannot convert a Symbol value to a number
(3)Symbol 值可以转为布尔值, 但不能转为数值.
- var s1 = Symbol();
- Boolean(s1);//true
- Number(s1);//TypeError: Cannot convert a Symbol value to a number
二, 作为属性名的 Symbol
由于每一个 Symbol 值都是不相等的, 这意味着 Symbol 值可以作为标识符, 对于对象的属性名, 就能保证不会出现同名的属性.
这对于一个对象由多个模块构成的情况非常有用, 能防止某一个键被不小心改写或覆盖.
- // 将对象的属性名设置为 Symbol 值
- var sy = Symbol();
- var obj = {};
- obj[sy] = 'hello';
- // 或者
- var a = {
- [sy]: 'hello'
- };
注意:
(1)Symbol 值作为属性名时, 不能用点运算符.
- var sy = Symbol();
- var obj = {
- };
- obj[sy] = 'hello';
- obj[sy];//"hello"
- obj.sy;//undefined
上例中, 因为点运算后面总是字符串, 所有不会读取到'hello' , 而是 undefined.
(2) 在对象内部, 用 Symbol 值定义属性时, Symbol 值必须放在方括号之中. 如果不放在方括号中, 该属性名就是字符串, 而不知 Symbol 值.
(3)Symbol 值作为属性名时, 该属性还是公开属性, 不是私有属性.
Symbol 类型还可以用于定义一组常量, 保证这组常量的值都是不相等的.
常量使用 Symbol 值对打的好处, 就是其他任何值都不可能有相同的值了.
三, 属性名的遍历
Symbol 作为属性名, 该属性不会出现在 for...in,for...of 循环中, 也不会被 Object.keys(),Object.getOwnPropertyName,JSON.stringify() 返回. 但是它不是私有属性.
Object.getOwnPropertySymbols() 方法, 可以获取指定对象的所有 Symbol 属性名. 该方法返回一个数组, 成员是当前对象的所有用作属性名的 Symbol 值.
Reflect.ownkeys() 方法可以返回所有类型的键名, 包括常规键名和 Symbol 键名.
- var a = Symbol('a'), b = Symbol('b');
- var obj = {
- [a]: 'hello',
- [b]: 'world',
- c: '123'
- };
- Object.getOwnPropertyNames(obj);//["c"]
- Object.getOwnPropertySymbols(obj);// [Symbol(a), Symbol(b)]
- Reflect.ownKeys(obj);// ["c", Symbol(a), Symbol(b)]
由于以 Symbol 值作为名称的属性, 不会被常规方法遍历得到. 可以利用这个特性, 为对象定义一些非私有的, 但又只希望内部使用的属性方法.
四, Symbol.for(),Symbol.keyFor()
1.Symbol.for()
如果希望重新使用同一个 Symbol 值, Symbol.for() 方法可以做到这一点. 他接受一个字符串作为参数, 然后搜索有没有以该参数作为名称的 Symbol 值. 如果有, 就返回这个 Symbol 值, 如果没有就新建并返回以该字符串为名称的 Symbol 值.
- var s1 = Symbol.for('foo');
- var s2 = Symbol.for('foo');
- s1 === s2;//true
Symbol.for() 与 Symbol() 的区别:
两个方法都会生成新的 Symbol. 但是 Symbol.for() 不会每次都会生成新值, 而是先检查给定的 key 是否存在, 如果不存在才会新建一个值. 但是 Symbol() 每次都会新建一个值, 没有登记机制.
- Symbol.for('foo') === Symbol.for('foo');//true
- Symbol('foo') === Symbol('foo');//false
注: Symbol.for() 为 Symbol 值登记的名字, 是全局环境的, 可以在不同的 iframe 或 service worker 中取到同一个值.
- iframe = document.createElement('iframe');
- iframe.src = String(Windows.location);
- document.body.appendChild(iframe);
- iframe.contentWindow.Symbol.for('foo') === Symbol.for('foo')
- // true
- 2. Symbol.keyFor()
Symbol.keyFor() 方法返回一个已经登记的 Symbol 类型值的 key.
- var s1 = Symbol('foo');//undefined
- Symbol.keyFor(s1);// undefined
- var s2 = Symbol.for('a');
- Symbol.keyFor(s2);// "a"
上例中 s1 属于未登记的 Symbol 值, 所以返回 undefined.
五, 内置的 Symbol 值
除了定义自己使用的 Symbol 值以外, ES6 还提供了 11 个内置的 Symbol 值, 指向语言内部使用的方法.
// 这点等看了 class 部分来回来补上
来源: http://www.qdfuns.com/article/46690/ddfcd588c7e20da07af664a67da51e96.html