声明
背景
JavaScript中有两种数据类型:基本数据类型和对象(对象也包括函数),基本数据类型包括简单数据类型,比如Number(从整数到浮点数,从Infinity到NaN都属于Number类型)、Boolean、String、undefined、null(注意尽管typeof null === object,null仍然是一个基本数据类型)。
基本数据类型的值是不可以改变的,即不能更改变量的原始值,当然可以重新对变量进行赋值。例如,代码:
let x = 1; x++;
虽然,你通过重新赋值改变了变量x的值,但是变量的原始值1仍没有被改变。
一些语言,比如C语言,有按引用传递和按值传递的概念。JavaScript也有类似的概念,它是根据传递数据的类型推断出来的。如果将值传入一个函数,则在函数中重新对它赋值不会修改它在调用位置的值。但是,如果你修改的是基本数据的值,那么修改后的值会在调用它的地方被修改。
考虑下面的例子:
function primitiveMutator(val) { val = val + 1; } let x = 1; primitiveMutator(x); console.log(x); //1 function objectMutator(val) { val.prop = val.prop + 1; } let obj = {prop: 1}; objectMutator(obj); console.log(obj.prop); //2
基本数据类型(NaN除外)总是与另一个具有相同值的基本数据类型完全相等。如下:
const first = "abc" + "def"; const second = "ab" + "cd" + "ef"; console.log(first === second); //true
然而,构造两个值相同的非基本数据类型则得到不相等的结果。我们可以看到发生了什么:
const obj1 = { name: "666"}; const obj2 = {name: "666"}; console.log(obj1 === obj2); //false
对象在JavaScript中扮演着重要的角色,几乎所有地方可以见到它们的身影。对象通常是键值对的集合,然而这种形式的最大限制是:对象的键只能是字符串,知道Symbol出现这一限制才得到解决。如果我们使用非字符串的值作为对象的键,该值会被强制转换为字符串。下面的程序中可以看到这种强制转换:
const obj = {}; obj.foo = "foo"; obj['bar'] = 'bar'; obj[2] = 2; obj[{}] = 'someobj'; console.log(obj);
Symbol是什么?
现在既然我们已经知道了基本数据类型是什么,也就终于可以定义Symbol了。
Symbol是不能被重新创建的基本数据类型。在这种情况下,Symbol类似与对象,因为对象创建多个实例也将导致不完全相等的值。但是,Symbol也是基本数据类型,因为它不能被改变。下面是Symbol用法的一个例子:
const s1 = Symbol(); const s2 = Symbol(); console.log(s1 === s2); //false
当实例化一个Symbol值时,有一个可选的首选参数,你可以赋值一个字符串。此值用于调试代码,不会真正影响Symbol本身。
const s1 = Symbol('debug'); const str = 'debug'; const s2 = Symbol('xxxy'); console.log(s1 === str); //false console.log(s1 === s2); //false console.log(s1); //Symbol(debug)
Symbol作为对象属性
symbol还有一个重要的用法,他们可以被当作对象中的键!下面是一个在对象中使用Symbol作为键的例子:
const obj = {}; const sym = Symbol(); obj[sym] = 'foo'; obj.bar = 'bar'; console.log(obj); console.log(sym in obj); console.log(obj[sym]); console.log(Object.keys(obj));
注意,Symbol键不会被在Object.keys()返回。也是为了满足向后兼容性。旧版本的JavaScript中没有Symbol数据类型,因此不应该从旧的Object.keys()方法中被返回。
乍一看,这就像是可以用Symbol在对象上创建私有属性!许多其他编程语言可以在其类中有私有属性,而JavaScript却遗漏了这种功能,长期以来被视为其语法的一种缺点。
不幸的是,与该对象交互的代码仍然可以访问对象那些键为Symbol的属性。甚至是在调用代码自己无法访问Symbol的情况下也有可能发生。例如,Reflect.ownKeys()方法能够得到一个对象的所有键的列表,包括字符串和Symbol:
function tryToAddPrivate(obj) { obj[Symbol('Pseudo Private')] = 42; } const obj = { prop: 'hello' }; tryToAddPrivate(obj); console.log(Reflect.ownKeys(obj)); console.log(obj[Reflect.ownKeys(obj)[1]]); // 42
防止属性名冲突
后续请查看原文。。。