【JavaScript】Symbol

本文介绍了ES6中新增的数据类型Symbol的特性,包括其不可变性、用于避免属性冲突、全局符号注册表的使用、与for...of循环和内置符号的关联。重点讲解了如何创建、重用符号,以及在对象属性和方法中的应用。
摘要由CSDN通过智能技术生成

Symbol

Symbol (符号) 是 ES6 新增的基本数据类型

  • 符号实例是唯一、不可变的。
  • 符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险。
const symbol = Symbol();
console.log("type", typeof symbol); // type symbol
console.log("value", symbol); // value Symbol()



基本使用

调用 Symbol 函数时,可以传入一个字符串参数作为对符号的描述(description)。但是,这个字符串参数与符号定义或标识完全无关:

const symbol1 = Symbol();
const symbol2 = Symbol();
console.log(symbol1 == symbol2); // false

const symbol3 = Symbol("foo");
const symbol4 = Symbol("foo");
console.log(symbol3 == symbol4); // false

如果参数不是字符串,会进行隐式类型转换:

const sym = Symbol({});
console.log(sym); // Symbol([object Object])



使用全局符号注册表

如果需要共享和重用符号实例,那么可以用一个字符串作为键,使用 Symbol.for 方法在全局符号注册表中创建并重用符号。

第一次使用某个字符串调用时,它会检查全局符号注册表,发现不存在对应的符号,于是就会创建一个新符号实例并添加到注册表中。后续使用相同字符串的调用同样会检查注册表,发现存在与该字符串对应的符号,然后就会返回该符号实例。

const fooGlobalSymbol = Symbol.for("foo"); // 创建新符号
const otherFooGlobalSymbol = Symbol.for("foo"); // 重用已有符号
console.log(fooGlobalSymbol === otherFooGlobalSymbol); // true

即使采用相同的符号描述,在全局注册表中定义的符号跟使用 Symbol 定义的符号也并不等同:

const localSymbol = Symbol("foo");
const globalSymbol = Symbol.for("foo");
console.log(localSymbol === globalSymbol); // false

全局注册表中的符号必须使用字符串键来创建,因此作为参数传给 Symbol.for 的任何值都会被转换为字符串。此外,注册表中使用的键同时也会被用作符号描述。

const emptyGlobalSymbol = Symbol.for();
console.log(emptyGlobalSymbol); // Symbol(undefined)

可以使用 Symbol.keyFor 来查询全局注册表,这个方法接收符号作为参数,返回该全局符号对应的字符串键。如果查询的不是全局符号,则返回 undefined:

// 创建全局符号
const s = Symbol.for("foo");
console.log(Symbol.keyFor(s)); // foo

如果传给 Symbol.keyFor 的不是符号,则该方法抛出 TypeError:

Symbol.keyFor(123); // TypeError: 123 is not a symbol



使用符号作为属性

凡是可以使用字符串或数值作为属性的地方,都可以使用符号。

const s1 = Symbol("a"),
    s2 = Symbol("b"),
    s3 = Symbol("c"),
    s4 = Symbol("b");

const obj = { [s1]: "a" };
console.log(obj); // {Symbol(a): 'a'}

obj[s2] = "b";
console.log(obj); // {Symbol(a): 'a', Symbol(b): 'b'}

Object.defineProperty(obj, s3, { value: "c" });
console.log(obj); // {Symbol(a): 'a', Symbol(b): 'b', Symbol(c): 'c'}

Object.defineProperties(obj, { [s4]: { value: "d" } });
console.log(obj); // {Symbol(a): 'a', Symbol(b): 'b', Symbol(c): 'c', Symbol(b): 'd'}

因为符号属性是对内存中符号的一个引用,所以直接创建并用作属性的符号不会丢失。但是,如果没有显式地保存对这些属性的引用,那么必须遍历对象的所有符号属性才能找到相应的属性键:

const o = {
    [Symbol("foo")]: "fooval",
    [Symbol("bar")]: "barval",
};

console.log(o); // {Symbol(foo): "foo val", Symbol(bar): "bar val"}

const barSymbol = Object.getOwnPropertySymbols(o).find((symbol) =>
    symbol.toString().match(/bar/)
);
console.log(barSymbol); // Symbol(bar)



遍历 Symbol 属性

const gender = Symbol("male");
const obj = {
    name: "superman",
    [gender]: "myGender",
};
console.log(obj[gender]); // myGender
  1. 使用 for in 遍历 obj 的常规属性:
const traverseObjByForIn = (obj) => {
    for (let key in obj) {
        console.log(key); // name
    }
};
traverseObjByForIn(obj);
  1. 使用 Object.keys 遍历 obj 的常规属性:
const traverseObjByObjectKeys = (obj) => {
    Object.keys(obj).forEach((key) => {
        console.log(key); // name
    });
};
traverseObjByObjectKeys(obj);
  1. 使用 Object.getOwnPropertyNames 获取所有常规属性:
const traverseObjByObjectGetOwnPropertyNames = (obj) => {
    console.log("Object.getOwnPropertyNames :");
    Object.getOwnPropertyNames(obj).forEach((key) => {
        console.log(key); // name
    });
};
traverseObjByObjectGetOwnPropertyNames(obj);
  1. 使用 Object.getOwnPropertySymbols 获取所有 Symbol 属性:
const traverseObjByObjectGetOwnPropertySymbols = (obj) => {
    console.log("Object.getOwnPropertySymbols :");
    Object.getOwnPropertySymbols(obj).forEach((key) => {
        console.log(key); // Symbol(male)
    });
};
traverseObjByObjectGetOwnPropertySymbols(obj);
  1. 使用 Reflect.ownKeys 获取所有属性:
const traverseObjByReflectOwnKeys = (obj) => {
    console.log("Reflect.ownKeys :");
    Reflect.ownKeys(obj).forEach((key) => {
        console.log(key); // name Symbol(male)
    });
};
traverseObjByReflectOwnKeys(obj);
  1. 使用 Object.getOwnPropertyDescriptors 获取所有属性的描述对象:
Object.getOwnPropertyDescriptors(obj);
{
    name: {
        value: 'superman',
        writable: true,
        enumerable: true,
        configurable: true,
    },
    [Symbol(male)]: {
        value: 'myGender',
        writable: true,
        enumerable: true,
        configurable: true,
    },
}



注意事项

  • Symbol 值不能进行数学运算/字符串拼接:
const sum = Symbol();
console.log(sum + 1); // TypeError: Cannot convert a Symbol value to a number
console.log(sum + ""); // TypeError: Cannot convert a Symbol value to a string
  • Symbol 值可以显式转化为 String / Boolean 类型,但不能转为 Number 类型:
const sum = Symbol();
console.log(String(sum)); // Symbol()
console.log(Boolean(sum)); // true
// console.log(Number(sum)); // TypeError: Cannot convert a Symbol value to a number

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JS.Huang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值