Symbol 类型
Symbol(符号) 是 ECMAScript6
新增的原始数据类型, 并且Symbol实例唯一不可变。
用于创建唯一的标识符 ,以确保属性名的唯一性,从而避免命名冲突。
Symbol 的基本用法
- 创建
Symbol
需要使用Symbol()
函数初始化。 - 因为
Symbol
本身也是原始数据类型,所以typeof
操作符返回的是symbol
Symbol()
没有字面量语法,
let sym = Symbol();
console.log(typeof sym); // symbol;
console.log(typeof sym === "symbol"); // true;
调用Symbol()
函数的时候,也可以传入一个字符串参数作为对符号的描述(description)。可以通过这个描述字符串来调试代码。
但是这个字符串参数与符号定义或标识完全无关。
let sym1 = Symbol();
let sym2 = Symbol();
let fooSym1 = Symbol("foo");
let fooSym2 = Symbol("foo");
console.log(sym1 === sym2); //false
console.log(fooSym1 === fooSym2); //false
Symbol
函数不能与 new
关键字一起作为构造函数使用。 这样做是为了避免创建Symbol
包装对象。像使用Number
,Boolean
,String
那样,
它们都支持构造函数并且可用于初始化包含原始值的包装对象。
let myBoolean = new Boolean();
console.log(typeof myBoolean); //Object
let myString = new String();
console.log(typeof myString); //Object
let myNumber = new Number();
console.log(typeof myNumber); //Object
let mySymbol = new Symbol(); //TypeEror: Symbol is not a constructor
包装对象
三种原始类型的值 Number
,Boolean
,String
在一定条件下,也会自动转为对象,也就是原始类型的“包装对象”。
所谓“包装对象”,就是Number
,Boolean
,String
三个原生对象。这三个原生对象可以把原始类型的值变成(包装成)对象。
let v1 = new Number(123);
let v2 = new String("abc");
let v3 = new Boolean(true);
上面代码中,基于原始类型的值,生成了三个对应的包装对象。
typeof v1; // "object"
typeof v2; // "object"
typeof v3; // "object"
v1 === 123; // false
v2 === "abc"; // false
v3 === true; // false
包装对象的最大目的,首先是使得 JavaScript 的对象涵盖所有的值,其次使得原始类型的值可以方便地调用某些方法
Number
,Boolean
,String
如果不作为构造函数调用(即调用时不加new
),常常用于将任意类型的值转为数值、字符串和布尔值。
Number(123); // 123
String("abc"); // "abc"
Boolean(true); // true
使用全局符号注册表
如果运行的不同部分需要共享重用的Symbol
实例。可以使用一个字符串作为 key
, 在全局符号注册表中创建并且复用Symbol
实例
为此我们可以使用Symbol.for()
这个方法
let fooGlobaSymbol = Symbol.for("foo");
console.log(typeof fooGlobaSymbol); // symbol;
第一次使用某个字符串调用的时候,它会检查全局运行时注册表,发现不存在的Symbol
实例,于是就会生成一个新的Symbol
实例。
并且添加到全局注册表中。后续使用相同字符串的调用同样会检查注册表。发现存在与该字符串对应的符号,返回这个Symbol
实例。
let fooGlobaSymbol = Symbol.for("foo");
console.log(fooGlobaSymbol === Symbol.for("foo")); //true
即使采用相同的符号描述, 在全局注册表中定义的符号跟使用Symbol()
定义的Symbol
实例也不相同
let fooGlobaSymbol = Symbol.for("foo");
console.log(fooGlobaSymbol === Symbol("foo")); //false
全局注册表中的符号必须使用字符串来创建,因此作为参数传给 Symbol.for()
的任何值都会被转换为字符串,此外注册表中使用的 key
,
同时也会被用作符号描述。
let emptyGlobaSymbol = Symbol.for();
console.log(emptyGlobaSymbol); // Symbol(undefined)
还可以使用Symbol.keyFor()
查询全局注册表中 Symbol
对应的 key
。
如果查询的不是全局的Symbol
, 则返回 undefined。
let fooGlobalSymbol = Symbol.for("foo");
console.log(Symbol.keyFor(fooGlobalSymbol)); // foo
let barSymbol = Symbol("bar");
console.log(Symbol.keyFor(barSymbol)); // undefined
//如果传给`Symbol.keyFor()`的不是 `Symbol`,则该方法就会抛出异常
console.log(Symbol.keyFor(123)); // 123 is not a symbol
使用符号作为属性
凡是可以使用字符串或者数值作为属性的地方,都可以使用Symbol
符号。
这就包括了对象字面量属性和Object.defineProperty()
和Object.definePropertys()
定义的属性
对象字面量只能在计算属性
语法中使用Symbol
符号作为属性
let s1 = Symbol("foo");
let s2 = Symbol("bar");
let s3 = Symbol("baz");
let s4 = Symbol("qux");
let o = {
[s1]: "foo val",
};
console.log(o); // {[Symbol(foo)]:"foo val"}
Object.defineProperty(o, s2, {
value: "bar val",
});
console.log(o); // {[Symbol(foo)]:"foo val",[Symbol(bar)]:"bar val"}
Object.defineProperties(o, {
[s3]: { value: "baz val" },
[s4]: { value: "qux val" },
});
console.log(o); // {[Symbol(foo)]:"foo val",[Symbol(bar)]:"bar val",[Symbol(baz)]:"baz val",[Symbol(qux)]:"qux val"}
Object.getOwnPropertyNames()
返回对象实例的常规属性数组 (包括不可枚举属性,但不包括使用 Symbol 值作为名称的属性和原型中的属性),Object.getOwnPropertySymbols()
返回对象实例的**Symbol
符号属性的数组**。Object.getOwnPropertyDescriptors()
返回同时包含常规和Symbol
符号属性描述符的对象。Reflect.ownKeys()
会返回两种类型的健Object.keys()
返回对象实例 可枚举的字符串键属性名 组成数组 (但不包括使用 Symbol 值作为名称的属性和原型中的属性)
let s1 = Symbol("foo");
let s2 = Symbol("bar");
let o = {
[s1]: "foo val",
[s2]: "bar val",
baz: "baz val",
qux: "qux val",
};
Object.defineProperty(o, "test", {
value: "test val",
enumerable: false,
});
console.log(Object.getOwnPropertySymbols(o)); // [Symbol(foo),Symbol(bar)]
console.log(Object.getOwnPropertyNames(o)); // ['baz','qux','test']
console.log(Object.getOwnPropertyDescriptors(o)); // {baz: {…}, qux: {…}, test: {…}, Symbol(foo): {…}, Symbol(bar): {…}}
console.log(Object.keys(o)); // ['baz','qux']
console.log(Reflect.ownKeys(o)); // ['baz','qux','test',Symbol(foo),Symbol(bar)]