一、关于Symbol
ES5中对象属性名都是字符串,这样可能会导致属性名的冲突,因此在ES6中引入了新类型Symbol,表示独一无二的值,作为JavaScript语言的第7种类型。
创建Symbol
Symbol函数不能通过new命令来创建,因为生成的Symbol是一个原始值,不是引用值
{
let sym = Symbol();
console.log(typeof sym); // symbol
}
Symbol函数可以接受一个字符串作为参数,表示对Symbol实例的描述,主要是为了转成字符串时比较容易区分。
{
let sym = Symbol("aa");
console.log(sym,sym.toString()); // Symbol(aa) "Symbol(aa)"
}
如果Symbol的参数是一个对象,就会调用其toString方法,将其转换为字符串,然后才生成一个Symbol值。
{
const obj = {
toString() {
return 'abc';
}
};
const sym = Symbol(obj);
console.log(sym);
}
Symbol函数的参数知识表示当前Symbol值的描述,相同参数的Symbol函数也是不相等的
{
let s1 = Symbol('foo');
let s2 = Symbol('foo');
console.log('s1==s2', s1 == s2); // s1==s2 false
}
如果需要使用同一个Symbol值,就需要通过Symbol的.for创建一个Symbol值,Symbol.for()方法接受一个字符串,然后搜索有没有以该参数作为名称的Symbol值,如果有则返回这个Symbol值,如果没有就新建并返回一个以该字符串为名称的Symbol值。
Symbol.keyFor方法返回一个已登记的Symbol类型值的key,也就是该Symbol的描述,只有Symbol.for才会有,因为Symbol.for是定义在全局的
{
let sym = Symbol.for("des");
let sym2 = Symbol.for("des");
console.log(sym == sym2); // true
console.log(Symbol.keyFor(sym)); // des
}
二、作为属性名的Symbol
Symbol作为对象属性名保证不会出现同名的属性,这对一个对象由多个模块构成的情况非常有用,能防止一个key被不小心改写或者覆盖。
{
let proSym = Symbol();
let a = {};
a[proSym] = 'hello';
console.log(a[proSym]); // hello
let b = {
[proSym]: 'world'
};
console.log(b[proSym]); // world
let c = {};
Object.defineProperty(c, proSym, { value: 'here' });
console.log(c[proSym]); // here
}
Symbol值作为属性名时不能使用点运算符,只能通过[]来获取或设置
{
let proSym = Symbol();
let d = {
[proSym](name) {
console.log('hello' + name);
}
};
d[proSym]('xiaohua'); // helloxiaohua
}
三、属性名的遍历
Symbol作为属性名时,该属性不会出现在for in和for of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()返回,可以使用专有的Object.getOwnPropertySymbols()方法来获取指定对象的所有Symbol属性名,该方法返回一个数组,包含了当前对象的所有作用属性名的Symbol值。
{
let obj = {};
let a = Symbol('a');
let b = Symbol('b');
obj[a] = 'Hello';
obj[b] = 'World';
let objectSymbols = Object.getOwnPropertySymbols(obj);
console.log(objectSymbols);
}
三种方法遍历Symbol的属性
{
let obj = {};
let a = Symbol('a');
let b = Symbol('b');
obj[a] = 'Hello';
obj[b] = 'World';
let foo = Symbol('foo');
Object.defineProperty(obj, foo, {
value: 'foobar'
});
console.log(obj); // {Symbol(a): "Hello", Symbol(b): "World", Symbol(foo): "foobar"}
for (let i in obj) {
console.log(i); // 没有,不会被检测到
}
let r1 = Object.getOwnPropertyNames(obj);
console.log(r1); // [] 空数组
let r2 = Object.getOwnPropertySymbols(obj);
console.log(r2); // [Symbol(a), Symbol(b), Symbol(foo)]
}
Reflect.ownKeys方法可以返回所有类型的键名,包括常规键名和Symbol键名
{
let o = {
[Symbol("key")]:1,
num:100
};
let re = Reflect.ownKeys(o);
console.log(re); // ["num", Symbol(key)]
}