javascript进阶知识21 - Symbol

200 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第13天,点击查看活动详情

ES6新增了一种数据类型--symbol(符号类型)。

Symbol是什么

Symbol是一种原始数据类型,并且是在ES6才新增的,它是唯一的、不可变的。

创建Symbol

创建Symbol是用Symbol()函数初始化。并且不能使用new关键字一起使用。

let sym = Symbol();
console.log(typeof sym);//symbol

let sym2 = new Symbol();  // Uncaught TypeError: Symbol is not a constructor

还可以给Symbol()函数传递字符串参数作为符号的描述。但是,这个符号的参数与符号定义或者标识完全无关。并且,符号是唯一的

let s1 = Symbol('a');
let s2 = Symbol('a');
console.log(s1 === s2); //false

console.log(s1); // Symbol('a')
console.log(s2); // Symbol('a')

从上面我们可以看出,Symbol()函数不是一个构造函数,应该是一个工厂函数,调用这个函数后,就会return一个符号。

description

每一个符号都有一个属性description,它会返回该符号的描述

let sym = Symbol('ly');
console.log(sym.description); // 'ly'

使用全局符号注册表

Symbol.for()对每一个字符串键(就是用一个字符串作为健,并且参数里面的任何值都会被转成String类型)都执行幂等操作。

幂等操作:多次操作的结果与一次操作的结果相同。

Symbol.for()第一次使用某个字符串调用时,它会检查全局运行时注册表,发现不存在对应的符号,于是就会生成一个新符号实例并添加到注册表中。后续使用相同字符串的调用同样会检查注册表,发现存在与该字符串对应的符号,然后就会返回该符号实例。

let s1 = Symbol.for('ll');
let s2 = Symbol.for('ll');
console.log(s1 === s2); //true

let s3 = Symbol.for('lly');
console.log(s1 === s3); //false

但是只有都使用Symbol.for()且参数相同定义的符号才会相同,如果一个使用Symbol.for(),一个使用Symbol(),哪怕参数相同也不会相同。(虽然都是符号,但是Symbol.for()是存放在符号注册表中的

let s1 = Symbol.for('ly');
let s2 = Symbol('ly');
console.log('s1 === s2'); //false

Symbol.keyFor()

可以使用Symbol.keyFor()来查询全局符号注册表,接收符号为参数,返回该符号的字符串键。

let s1 = Symbol.for('ly');
console.log(Symbol.keyFor(s1)); // ly

该方法只对全局符号注册表有用,如果是普通符号:

let s1 = Symbol('ly');
console.log(Symbol.keyFor(s1)); //undefined

如果是其他类型的值:

let a = 10;
console.log(Symbol.keyFor(a));// TypeError: 10 is not a symbol

Symbol的作用

那么这个Symbol的作用是啥呢?

------》 因为Symbol是唯一的嘛,我们常常使用Symbol来创建唯一的记号,进而用来作非字符串形式的对象属性。

假如一个班上有两个同学的名字是一样的,都叫张三,而现在我们需要统计这个班上每一个同学的期末成绩,那么如果使用对象来存储的话,属性名相同的属性就会被后面的替换掉。

let obj1 = {
    name:'张三'
}
let obj2 = {
    name:'张三'
}
let grade = {
    [obj1.name]:{
        math:95,
        english:89
    },
    [obj2.name]:{ 
        math:87,
        english:98
    }
}
console.log(grade); 
//  {张三: {…}}
      - 张三: {math87english98}
      - Prototype]]: Object

我们发现grade对象中只有后面个张三的属性,前一个被替换掉了。再简单看一下:

let obj = {
    zhangsan:10,
    zhangsan:22
}
console.log(obj) //{zhangsan: 22}

这个时候我们就可以使用Symbol了。

let obj = {
    [Symbol('zs')]:10,
    [Symbol('zs')]:22
}
console.log(obj);
// {Symbol(zs): 10, Symbol(zs): 22}

console.log(obj[Symbol('zs')]); //undefined

但是这样我们就不能取出这个值了,所以一般我们会用一个变量来接收这个值。因为console.log(obj[Symbol('zs')]);里面的Symbol('zs')又是一个新的唯一的符号了。

let obj1 = {
    name:'张三',
    key:Symbol('张三')
}
let obj2 = {
    name:'张三',
    key:Symbol('张三')
}
let grade = {
    [obj1.key]:{
        math:95,
        english:89
    },
    [obj2.key]:{ 
        math:87,
        english:98
    }
}
console.log(grade); 

image.png

同时还可以取出来。

.....接上面的代码....
console.log(grade); 
console.log(grade[obj1.key]);   // {math: 95, english: 89}
console.log(grade[obj2.key]);   // {math: 87, english: 98}

此外,使用Symbol符号作为对象的属性名,该属性名是属于被保护的,也就是不能使用key..inkey..of访问得到。

let sym = Symbol();
let obj = {
    [sym]:'符号',
    name:'ly',
    sayName() {
        console.log(this.name)
    }
}
for(let key in obj) {
    console.log(key)
}
// name
// sayName

可以使用 Object.getOwnPropertySymbols() 方法得到对象的符号属性,但是不能得到普通属性。

let sym = Symbol();
let obj = {
    [sym]:'符号',
    name:'ly',
    sayName() {
        console.log(this.name)
    }
} 
console.log(Object.getOwnPropertySymbols(obj)); // [Symbol()]