symbol 类型

119 阅读3分钟

这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情

前言

根据规范,只有两种原始类型可以用作对象属性键:

  • 字符串类型
  • symbol 类型

否则,如果使用另一种类型,例如数字,它会被自动转换为字符串。所以 obj[1] 与 obj["1"] 相同,而 obj[true] 与 obj["true"] 相同。 这就会导致属性名冲突,因此ES6引入新的原始数据类型Symbol

symbol简介

“symbol” 值表示唯一的标识符。

可以使用 Symbol() 来创建这种类型的值:

let id = Symbol();
  • 也可以在symbol内部放入一个字符串,在代码调试时会方便很多(内部的字符串至少一个标签,因此即使内部字符串一样创建的symbol变量仍然不相同)
let id1 = Symbol("id");
let id2 = Symbol("id"); 
let s2 = Symbol('bar');

alert(id1 == id2);    // false

id1.toString() //'Symbol('id')'
id2.toString() //'Symbol('id')'
s2.toString() // "Symbol(bar)"
alert(id1.description); // id

  • 这里可以看见,里面的字符串是一个区分的作用,toString()查看我们调用的是什么(这里是转化成了字符串)或者获取 symbol.description 属性,只显示描述(description):

注意事项

无法和其他类型处理

let sym = Symbol('My symbol');

"your symbol is " + sym
// TypeError: can't convert symbol to string
`your symbol is ${sym}`
// TypeError: can't convert symbol to string

  • 无法直接转化成字符串和布尔值,不能转化成数字类型
  • 转化成字符串 String(sym) 或者 sym.toString()
  • 转化成布尔值 Boolean(sym) 或者 !!sym

作为属性名称

let mySymbol = Symbol();

// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';

// 第二种写法
let a = {
  [mySymbol]: 'Hello!'
};

// 第三种写法
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });

// 以上写法都得到同样结果
a[mySymbol] // "Hello!"

这是Symbol作为属性的使用场景

  • 下面这种事特殊的一种情况
a.mySymbol = 'Hello!';
a[mySymbol] // undefined
console.log(a['mySymbol']) // "Hello!"
  • 这里我们就知道,实际上a. 并不是Symbol值而是一个字符串。因此,Symbol类型不能使用点运算符(点运算符后面总是字符串)。Symbol定义属性需放在[]中,这杨才不会是字符串

隐藏属性

  • symbol 允许我们创建对象的“隐藏”属性,代码的任何其他部分都不能意外访问或重写这些属性。
let id = Symbol("id"); 
let user = { name: "John", age: 30, [id]: 123 };
for (let key in user) 
alert(key); // name, age(没有 symbol) 
alert("Direct: " + user[id]); // 使用 symbol 直接访问 Direct: 123

Object.keys(user) 也会忽略它们。这是一般“隐藏符号属性”原则的一部分。如果另一个脚本或库遍历我们的对象,它不会意外地访问到符号属性。

相反,Object.assign 会同时复制字符串和 symbol 属性:

let id = Symbol("id");
let user = {
  [id]: 123
};

let clone = Object.assign({}, user);

alert( clone[id] ); // 123

这里并不矛盾,就是这样设计的。这里的想法是当我们克隆或者合并一个 object 时,通常希望 所有 属性被复制(包括像 id 这样的 symbol)。

// 通过 name 获取 symbol
let sym = Symbol.for("name"); 
let sym2 = Symbol.for("id"); 
let globalSymbol = Symbol.for("name"); 
let localSymbol = Symbol("name");
alert( Symbol.keyFor(globalSymbol) ); // name,全局 symbol 
alert( Symbol.keyFor(localSymbol) ); // undefined,非全局 

// 通过 symbol 获取 name 
alert( Symbol.keyFor(sym) ); // name 
alert( Symbol.keyFor(sym2) ); // id
  • 对于全局 symbol,Symbol.for(key) 按名字返回一个 symbol。相反,通过全局 symbol 返回一个名字,我们可以使用 Symbol.keyFor(sym)