JS 中的 Symbol 类型及其应用

168 阅读4分钟

概述

ES5的对象属性名都是字符串,这就很容易造成属性名的冲突。比如,我们使用了一个他人提供的对象,但又想为这个对象添加新的方法,新方法的名字就有可能和现有方法产生冲突。为了从根本上防止属性名冲突,ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。本文将详细介绍 Symbol 类型的概念、用途以及在实际开发中的应用。

什么是 Symbol?

Symbol 是一种基本数据类型,它是 ES6 引入的一种新的原始数据类型。每个 Symbol 值都是唯一的,即使它们具有相同的描述符。Symbol 的主要用途是作为对象属性的键,确保这些属性不会与其他属性发生冲突。

创建 Symbol

创建 Symbol 的方法非常简单,只需要调用 Symbol 函数即可。Symbol 函数可以接受一个可选的字符串参数,作为该 Symbol 的描述符,但这并不影响 Symbol 的唯一性。

注意:Symbol函数前不能使用new命令,否则会报错。这是因为生成的Symbol是一个原始类型的值,不是对象。也就是说由于Symbol值不是对象,所有不能添加属性。基本上,它是一种类似于字符串的数据类型

const sym1 = Symbol();
const sym2 = Symbol('description');
console.log(sym1); // Symbol()
console.log(sym2); // Symbol(description)

Symbol 特性

唯一性

Symbol 的核心特性是其唯一性。即使两个 Symbol 具有相同的描述符,它们仍然是不同的值。这种唯一性使得 Symbol 在对象属性命名中非常有用,特别是在大型项目中,多个开发者可能会使用相同的属性名,导致属性被意外覆盖。

javascript
const sym1 = Symbol('key');
const sym2 = Symbol('key');
console.log(sym1 === sym2); // false

Symbol 作为对象属性键

Symbol 最常见的用途是作为对象属性的键。由于 Symbol 的唯一性,可以确保这些属性不会与其他属性发生冲突。这在大型项目中尤为重要,因为多个开发者可能会在同一对象上添加属性,而 Symbol 可以避免这种冲突。

javascript
const name = "张三";
const classMates = {
  "cy": 1,
  "cy": 2, // 覆盖上面的cy
  [name]: "猛男",
  [Symbol('Mark')]: { grade: 50, gender: 'male' },
  [Symbol('olivia')]: { grade: 80, gender: 'female' },
  [Symbol('olivia')]: { grade: 80, gender: 'female' }, // 不会被覆盖
};

console.log(classMates[name], classMates.cy, classMates);

image.png

在这个例子中,classMates 对象中有多个属性,其中 cy 属性被下面的覆盖了,而由于每个 Symbol 都是唯一的,Symbol 类型的属性便不会被覆盖,。

访问 Symbol 属性

Symbol作为属性名,该属性不会出现在for...infor...of循环中,也不会被 Object.keys()Object.getOwnPropertyNames()返回。但它也不是私有属性,有一个 Object.getOwnPropertySymbols() 方法可以获取指定对象的所有Symbol属性名

const syms = Object.getOwnPropertySymbols(classMates);
console.log(syms);

const data = syms.map(sym => classMates[sym].grade);
console.log(data);

image.png

不可枚举性

    console.log(Object.getOwnPropertyDescriptors(classMates))

image.png

从上面的结果可以看到,尽管 Symbol 属性在对象的属性描述符中是可枚举的,但在实际遍历对象时,这些属性不会被枚举到。这是因为 Symbol 的设计目的是提供唯一值,而不是用于常规的属性遍历。

for (let [key, val] of Object.entries(classMates)) {
  console.log(key, val);
}

for (const person in classMates) {
  console.log(person, classMates[person]);
}

image.png

可以看到这里是没有打印出Symbol 的值的,这也就说明了它的不可枚举性

Symbol 的应用场景

  • 避免命名冲突: 在大型项目或多人协作环境中,不同的模块可能会定义同名的属性。如果这些属性都使用字符串作为键名,则很容易发生冲突。而 Symbol 由于其唯一性,可以有效避免这种情况。
  • 实现私有属性Symbol 可以用于实现类的私有属性,因为这些属性不会被外部代码访问到。
  • 定义唯一标识符:当需要为对象创建一个独一无二的标识符时,Symbol 是理想的选择。它保证了每个实例都有一个独特的标识符,这对于追踪对象实例特别有用。

小结

Symbol 是 JavaScript 中的一种基本数据类型,其核心特性是唯一性。Symbol 主要用于作为对象属性的键,确保这些属性不会与其他属性发生冲突。在大型项目中,Symbol 的这一特性尤为重要,因为它可以帮助开发者避免属性被意外覆盖的问题。此外,Symbol 还可以用于实现类的私有属性和内置的特殊行为。

c224676594aae6804009e3fe521b122.jpg