Javascript对象属性名分为两种类型,一种是string类型,另一种是symbol类型。symbol类似于标识符,是唯一的,在创建symbol类型变量的时候,我们可以为它添加描述,由于symbol值是唯一的,所以即使描述相同,它们也是不等的,例如:
let id1 = Symbol("id");
let id2 = Symbol("id");
alert(id1 == id2); // false
ps:
需要注意的是symbol类型的变量并不会自动转换为string类型,如果需要转换为string,必须使用toString(),例如:let id = Symbol("id");
alert(id.toString()); // Symbol(id), now it works
“隐藏”的属性
在多脚本的程序里,有时候我们会向同一个对象添加属性,如果我们所添加的属性名正好相同,那么后一个操作会重写前一个操作,这就造成了冲突。但由于symbol类型变量是唯一的,我们就可以避免这种情况,就好像当前脚本向对象添加属性后,不会对其他脚本的操作造成影响,有种隐藏的感觉。
let user = { name: "John" };
let id = Symbol("id");
user[id] = "ID Value";
alert( user[id] ); // we can access the data using the symbol as the key
使用symbol类型作为属性名相对于string类型的好处是不会造成冲突,重写的现象。
当然,我们也可以显式声明symbol,例如:
let id = Symbol("id");
let user = {
name: "John",
[id]: 123 // not just "id: 123"
};
for...in不会遍历出symbol属性
举个栗子:
let id = Symbol("id");
let user = {
name: "John",
age: 30,
[id]: 123
};
for (let key in user) alert(key); // name, age (no symbols)
// the direct access by the symbol works
alert( "Direct: " + user[id] );
使用for...in进行遍历的时候,symbol属性会被忽略掉,只能显式地调用。
let id = Symbol("id");
let user = {
[id]: 123
};
let clone = Object.assign({}, user);
alert( clone[id] ); // 123
ps:
其他类型的属性名在javascript会自动转换为string类型,例如:
let obj = {
0: "test" // same as "0": "test"
};
// both alerts access the same property (the number 0 is converted to string "0")
alert( obj["0"] ); // test
alert( obj[0] ); // test (same property)
全局symbol
在创建symbol变量的时候,我们可向其添加描述名,但是一个描述名可能对应多个symbol,为了使得一个描述名对应一个symbol变量,javascript引进了全局的symbol注册这个机制,也就是Symbol.for(key),其中key就是描述名,例子:
// read from the global registry
let id = Symbol.for("id"); // if the symbol did not exist, it is created
// read it again
let idAgain = Symbol.for("id");
// the same symbol
alert( id === idAgain ); // true
同时也有Symbol.keyFor(sym)来获取描述名,例如:
let sym = Symbol.for("name");
let sym2 = Symbol.for("id");
// get name from symbol
alert( Symbol.keyFor(sym) ); // name
alert( Symbol.keyFor(sym2) ); // id