Skip to main content

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)]