对于一项新知识,要知道它有什么用,解决了什么问题,那么才有研究它的动力。所以学习Symbol从它解决了什么问题出发。
“ES5 的对象属性名都是字符串,这容易造成属性名的冲突。比如,你使用了一个他人提供的对象,但又想为这个对象添加新的方法(mixin 模式),新方法的名字就有可能与现有方法产生冲突。如果有一种机制,保证每个属性的名字都是独一无二的就好了,这样就从根本上防止属性名的冲突。这就是 ES6 引入Symbol的原因。”
一开始这段话我好像看懂了,但是实际上那要怎么用???迷惑
那就把这段话翻译成代码吧
举个例子,你看上了公司前来的前台妹纸,想了解关于她的更多信息,于是就询问Hr同事,扫地阿姨,于是得到类似这样信息:
let info1 = {
name: '婷婷',
age: 24,
job: '公司前台',
description: '平时喜欢做做瑜伽,人家有男朋友,你别指望了'
}
let info2 = {
description: '这小姑娘挺好的,挺热情的,嘿嘿嘿……'
}
当我们汇总这位前台小姐的信息时,就发现,``` description:'这小姑娘挺好的,挺热情的,嘿嘿嘿……'``导致别人有男朋友这个重要的信息丢失了。
但是我们用了symbol之后
let info1 = {
name: '婷婷',
age: 24,
job: '公司前台',
[Symbol('description')]: '平时喜欢做做瑜伽,人家有男朋友,你别指望了'
}
let info2 = {
[Symbol('description')]: '这小姑娘挺好的,挺热情的,嘿嘿嘿……'
}
let target ={}
Object.assign(target,info1,info2)
console.log(target)
//age: 24
//job: "公司前台"
//name: "婷婷"
//Symbol(description): "平时喜欢做做瑜伽,人家有男朋友,你别指望了"
//Symbol(description): "这小姑娘挺好的,挺热情的,嘿嘿嘿……"
他的信息就被完整保留下来了。
啊,谢天谢地。终于弄懂Symbol是怎么解决命名冲突的了。
下面开始深入学习Symbol的用法
- Symbol是js基本数据类型,Symbol 值通过Symbol函数生成。不能用new运算符创建
var a = Symbol()
括号里面可以接收一个字符串参数,作为该实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。
var a = Symbol('foo')
a // Symbol(foo)
a.toString() // "Symbol(foo)"
-
Symbol()
返回值是唯一的,也就是,即使描述相同,他们也是独立的两个值。
Symbol('description') === Symbol('description'); // 返回值是false
这跟引用类型一样
var a = new Object()
var b = new Object()
a===b // false
如果是普通的基本类型,比如
var a = 'string'
var b = 'string'
a===b //true
- Symbol值不可以和其他类型的值进行运算,可以显示转换为字符串或者布尔值
let sym = Symbol('My symbol');
String(sym) // 'Symbol(My symbol)'
sym.toString() // 'Symbol(My symbol)'
let sym = Symbol();
Boolean(sym) // true
!sym // false
if (sym) {
// ...
}
Number(sym) // TypeError
sym + 2 // TypeError
一些属性和方法
- 读取Symbol的描述
description
const sym = Symbol('foo');
sym.description // "foo"
-
作为常量的Symbol
常量使用 Symbol 值最大的好处,就是其他任何值都不可能有相同的值了,因此可以保证上面的switch语句会按设计的方式工作。
emmm到底是怎样的特别适合呢?
比如
let a='a'+'b'
let b='ab'
a===b //true
这样case 'ab'中,变量a和b都是符合条件的,但是我们只想要b这种形式的。这个时候把b变成symbol就很有必要啦。
大概是这样吧
const COLOR_RED = Symbol();
const COLOR_GREEN = Symbol();
function getComplement(color) {
switch (color) {
case COLOR_RED:
return COLOR_GREEN;
case COLOR_GREEN:
return COLOR_RED;
default:
throw new Error('Undefined color');
}
}
-
作为属性名的Symbol
这也是一开头提到的那个例子
有4种写法
let mySymbol = Symbol();
// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';
// 第二种写法
let a = {
[mySymbol]: 'Hello!'
};
// 第三种写法
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });
// 以上写法都得到同样结果
a[mySymbol] // "Hello!"
以上三种,都保留了Symbol的引用值,就是一开头的mySymbol
。这些引用值还是需要不一样,才能正确引用对应的Symbol值。这样写引用方式也比较简单。
但是,这样跟我写
var obj = {
a:"a",
b:"b"
}
有什么区别,还不是要起不一样的引用值的名字!
所以就有了最开头例子的那种写法,
let info2 = {
[Symbol('description')]: '这小姑娘挺好的,挺热情的,嘿嘿嘿……'
}
不保留他的引用值,通过Object.getOwnPropertySymbols(对象名)
这个方法返回的是该对象中Symbol值的数组
let array = Object.getOwnPropertySymbols(target);
console.log(array);
console.log(target[array[0]]);
//Array [ Symbol(description), Symbol(description) ]
//'平时喜欢做做瑜伽,人家有男朋友,你别指望了'
在此插播一个小知识点
es6之前变量属性名称都是字符串型的
访问对象普通属性时,可以使用点运算符obj.a
或者obj['a']
进行访问。访问Symbol类型的对象属性时,用obj[a]
Symbol 值作为属性名时,该属性还是公开属性,不是私有属性。
-
属性名的遍历
照例科普遍历对象属性名的各种方法
→ 获得对象的属性
Symbol 作为属性名,该属性不会出现在for...in、for...of
循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()
返回。但是,它也不是私有属性,有一个Object.getOwnPropertySymbols
方法,可以获取指定对象的所有 Symbol 属性名。
就是上面的例子中使用过的
Reflect.ownKeys
方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。
let obj = {
[Symbol('my_key')]: 1,
enum: 2,
nonEnum: 3
};
Reflect.ownKeys(obj)
// ["enum", "nonEnum", Symbol(my_key)]
由于以 Symbol 值作为名称的属性,不会被常规方法遍历得到。我们可以利用这个特性,为对象定义一些非私有的、但又希望只用于内部的方法。
let size = Symbol('size');
class Collection {
constructor() {
this[size] = 0;
}
add(item) {
this[this[size]] = item;
this[size]++;
}
static sizeOf(instance) {
return instance[size];
}
}
let x = new Collection();
Collection.sizeOf(x) // 0
x.add('foo');
Collection.sizeOf(x) // 1
Object.keys(x)
Object.getOwnPropertyNames(x)
Object.getOwnPropertySymbols(x)
// [Symbol(size)]
看到这里,要去补一下js的class了。。。
-
Symbol的复用
因为每次使用Symbol()
方法,即使描述符相同,也不是同一个symbol了,想要复用symbol,即重新使用同一个 Symbol 值,Symbol.for方法可以做到这一点。它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建并返回一个以该字符串为名称的 Symbol 值。
let s1 = Symbol.for('foo');
let s2 = Symbol.for('foo');
s1 === s2 // true
感觉面试会问
Symbol()
和Symbol.for()
的区别:
虽然Symbol()
和Symbol.for()
都可以创建一个新的Symbol值,但是前者会被登记在全局环境中供搜索,后者不会。Symbol.for()
不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key是否已经存在,如果不存在才会新建一个值。比如,如果你调用Symbol.for("cat")
30 次,每次都会返回同一个 Symbol 值,但是调用Symbol("cat")
30 次,会返回 30 个不同的 Symbol 值。
Symbol.keyFor
方法返回一个已登记的 Symbol 类型值的key。(就是使用过Symbol.for()
)
let s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"
let s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined
小总结:
1.Symbol数据类型的引入,解决了变量名冲突的问题
2.Reflect.ownKeys(obj)可以返回对象所有属性,包括Symbol值
3.Object.getOwnPropertySymbols(obj)可以获得该对象上的Symbol值,返回一个数组
4.通过Object.for('key')在全局环境中查找key值,找到则返回那个值,找不到就在全局环境中注册并返回该值
5.在全局环境中注册过的Symbol值,可以使用Object.keyfor('变量名')返回该值都key值
先那么多吧。。。
网友评论