在ES5及早期版本中,语言包含5种原始类型:字符串型、数字型、布尔型、null和undefined。ES6引入了第6种原始类型:Symbol。在Symbol出现以前,人们一直通过属性名来访问所有属性,无论属性名由什么元素构成,全部通过一个字符串类型的名称来访问;私有名称原本是为了让开发者们创建非字符串属性名称而设计的,但是一般的技术无法检测这些属性的私有名称。
创建Symbol
所有原始值,除了Symbol意外都有各自的字面形式,例如布尔类型的true或数字类型的42。可以通过全局的Symbol函数创建一个Symbol,就像这样:
let firstName = Symbol();let person = {};person[firstName] = "Nicholas";console.log(person[firstName]); //"Nicholas"
在上面这段代码中,创建了一个名为firstName的Symbol,用它将一个新的属性赋值给person对象,每当你想访问这个属性时一定要用到最初定义的Symbol。记得要合理命名Symbol变量,这样可以轻松区分出它所指代的内容。
注意,由于Symbol是原始值,因此调用new Symbol()会导致程序抛出错误。也可以执行new Object()创建一个Symbol的实例。
Symbol函数**接受一个可选参数,其可以让你添加一段文本描述即将创建的Symbol,这段描述不可用于属性访问,但是建议你在每次创建Symbol时都添加这样一段描述,以便于阅读代码和调试Symbol程序。
let firstName = Symbol("first name");let person = {};person[firstName] = "Nicholas";console.log("first name" in person); //falseconsole.log(person[firstName]); //"Nicholasconsole.log(firstName); //"Symbol(first name)"
Symbol的描述被存储在内部的[[Description]]属性中,只有当调用Symbol的toString()方法时,才可以读取这个属性。在执行console.log()时,隐式调用了firstName的toString()方法,所以它的描述会被打印到日志中,但不能直接在代码里访问[[Description]]。
Symbol的使用方法
`所有使用可计算属性名的地方,都可以使用Symbol。前面我们看到的都是在括号中使用Symbol,事实上,Symbol也可以用于可计算对象字面量属性名、Object.defineProperty()方法和Object.defineProperties()方法的调用过程中。
let firstName = Symbol("first name");//使用一个可计算对象字面量属性let person = {[firstName] :"Nicholas"};//将属性设置为只读Object.defineProperty(person,firstName,{writable:false}); //Object.defineProperty() 方法会直接在一个//对象上定义一个新属性,//或者修改一个对象的现有属性, 并返回这个对象。let lastName = Symbol("last name");Object.defineProperties(person,{[lastName] : {value:"Zakas",writable:false}});console.log(person[firstName]); //"Nicholas"console.log(person[lastName]); //"Zakas"
在此实例中,首先通过可计算对象字面量属性语法为person对象创建了一个Symbol属性firstName。后面一行代码将这个属性设置为只读。随后,通过Object.defineProperties()方法创建一个只读的Symbol属性lastName,此处再次使用了对象字面量属性,但却是作为Object.defineProperties()方法的第二个参数使用。
Symbol共享体系
为了能够在不同的代码中共享同一个Symbol属性名,要使用Symbol.for()方法。它只接受一个参数,即即将创建的Symbol的字符串标识符,这个参数同样也被用作Symbol的描述:
let uid = Symbol.for("uid");let object = {};object[uid] = "12345";console.log(object[uid]); //"12345"console.log(uid); //"Symbol(uid)"
Symbol.for()方法,首先在全局Symbol注册表中搜索键为”uid”的Symbol是否存在,若存在,直接返回已有的Symbol;否则,创建一个新的Symbol,并使用这个键在Symbol注册表中注册,随即返回新创建的Symbol。
可以使用Symbol.keyFor()方法在Symbol全局注册表中检索与Symbol有关的键:
let uid = Symbol.for("uid");console.log(Symbol.keyFor(uid)); //"uid"let uid2 = Symbol.for("uid");console.log(Symbol.keyFor(uid2)); //"uid"let uid3 = Symbol("uid");console.log(Symbol.keyFor(uid3)); //undefined
uid和uid2都返回了”uid”这个键,而在Symbol全局注册表中不存在uid3这个Symbol,也就是不存在与之有关的键,所以最终返回undefined。
