1、概述
ES5对象属性名都是字符串容易造成属性名的冲突。
eg:var a = { name: 'lucy'};
a.name = 'lili';
这样就会重写属性
ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。
七种数据类型
JavaScript 在 1997 年被标准化时,就有 6 种数据类型,直到 ES6 出现之前,程序中的变量一定是以下 6 种数据类型之一:
Undefined
Null
Boolean
Number (包括 NAN)
String
Object
注意,Symbol函数前不能使用new命令,否则会报错。这是因为生成的Symbol是一个原始类型的值,不是对象
Symbol函数可以接受一个字符串作为参数,表示对Symbol实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。
var sym1 = Symbol();
var sym2 = Symbol("foo");
var sym3 = Symbol("foo");
console.log(sym1, sym2, sym3) //输出Symbol() Symbol(foo) Symbol(foo)
// 没有参数的情况
var s1 = Symbol();
var s2 = Symbol();
s1 === s2 // false
// 有参数的情况
var s1 = Symbol("foo");
var s2 = Symbol("foo");
s1 === s2 // false
Symbol值不能与其他类型的值进行运算
注:可以转为字符串,布尔值,不能转为数值
2、作为属性名的Symbol
var mySymbol = Symbol();
// 第一种写法
var a = {};
a[mySymbol] = 'Hello!';
// 第二种写法
var a = {
[mySymbol]: 'Hello!'
};
// 第三种写法
var a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });
// 以上写法都得到同样结果
a[mySymbol] // "Hello!"
我们可以用typeof判断某一个变量类型为Symbol类型:
typeof Symbol() === 'symbol' //输出:true
typeof Symbol('foo') === 'symbol' //输出: true
typeof Symbol.iterator === 'symbol' //输出 : true
如果我们不知道Symbol的实例名字是什么,我们怎么获取到Symbol值的呢, Symbol无法被for in , for of循环,以及Object.keys, Object.values 等都无法遍历到Symbol的属性; Object下给我们提供了一个
getOwnPropertySymbols;
let sym0 = Symbol("o_o?");
let obj = {
[sym0] : "heheda"
}
for( let prop of Object.getOwnPropertySymbols(obj) ) {
//prop就是Symbol的名字
console.log( obj[prop] ); //输出:heheda
};
或者用ES6提供的反射 : Reflect.ownKeys
let sym0 = Symbol("o_o?");
let obj = {
[sym0] : "heheda"
}
console.log( Reflect.ownKeys(obj) ); //输出:[ Symbol(o_o?) ]
Symbol.for和Symbol的唯一区别是 Symbol.for创建的两个实例可能相等, 根据Symbol的参数生成实例, 如果参数一样, 那么会返回同一个实例;
let foo = Symbol.for( "1111" );
let bar = Symbol.for("1111");
console.log( foo === bar ); //输出: true
//只有通过Symbol.for创建的对象,才能用keyFor找到原来的参数;
console.log(Symbol.keyFor(foo)) //会输出:1111
当出现 key1 === key2 时就会有 Symbol.for( key1 ) === Symbol.for( key2 ) 。这种对应关系甚至是跨 service worker 和 iframe 的。
let privateProperty1 = Symbol.for( 'firstName' );
let privateProperty2 = Symbol.for( 'firstName' );
myObject[ privateProperty1 ] = 'Dave';
myObject[ privateProperty2 ] = 'Zsolt';
console.log( myObject[ privateProperty1 ] );
// Zsolt
因为 Symbol 注册表中的 Symbol 值和字符串之间有一一对应的关系,所以我们也可以检索字符串键。使用 Symbol.keyFor 方法。
Symbol.keyFor( privateProperty1 );
> "firstName"
Symbol.keyFor( Symbol() );
> undefined
Symbol 作为半私有属性键
即使 Symbol 不能使属性私有,它们也能用作带有私有属性的符号。你可以使用 Symbol 来分隔公有和私有属性的枚举 ,Symbol 能使它更清楚。
const _width = Symbol('width');
class Square {
constructor( width0 ) {
this[_width] = width0;
}
getWidth() {
return this[_width];
}
}
只要你能隐藏 _width 就行了,隐藏 _width 的方法之一是创建闭包:
let Square = (function() {
const _width = Symbol('width');
class Square {
constructor( width0 ) {
this[_width] = width0;
}
getWidth() {
return this[_width];
}
}
return Square;
} )();
这样做的好处是,他人很难访问到我们对象的私有 _width 值,而且也能很好地区分,哪些属性是公有的,哪些属性是私有的。但这种方法的缺点也很明显:
通过调用 Object.getOwnPropertySymbols ,我们可以使用 Symbol 键。
如果要写很多的代码,这会使得开发者的体验不佳,访问私有属性不像 Java 或 TypeScript 那样方便。
如果你要用 Symbol 来表示私有字段,那你需要表明哪些属性不能被公开访问,如若有人试图违背这一规则,理应承担相应的后果。
内置的Symbol值
有11个方法,具体可以查看http://es6.ruanyifeng.com/#docs/symbol