JavaScript Symbol:理解这个唯一的标识符

更新日期: 2025-12-23 阅读: 60 标签: 标识

Symbol是ES6引入的新东西。它是JavaScript的第7种原始数据类型。前面6种是:字符串、数字、布尔值、null、undefined、BigInt。

JavaScript的数据类型分两大类:

  • 原始数据类型:上面说的7种,它们不可变,存在栈内存里

  • 引用数据类型:对象、数组、函数、日期、正则表达式、Map/Set等,它们可变,存在堆内存里


Symbol是什么?

Symbol表示独一无二的值。每个Symbol都是唯一的,不会重复。

// 创建Symbol
const sym1 = Symbol();
const sym2 = Symbol('描述文字'); // 可以加描述,方便调试
const sym3 = Symbol('描述文字');

console.log(sym2 === sym3); // false
// 即使描述相同,也是不同的Symbol


Symbol的核心特点

1. 唯一性

每个Symbol都是唯一的,这使它适合做对象属性名,可以避免名字冲突。

举个例子:
一个班级里,学生可能有同名,但学号是唯一的。Symbol就像学号,是唯一的标识符。

const obj = {
  [Symbol('id')]: 1,
  [Symbol('id')]: 2
};

// 获取所有Symbol属性
console.log(Object.getOwnPropertySymbols(obj)); 
// 输出两个不同的Symbol,虽然描述都是'id'

2. 隐藏性

Symbol属性不会出现在常规的对象遍历中。

普通属性就像客厅的家具,客人来都能看到。Symbol属性就像保险箱里的东西,存在但看不到。

const obj = {
  [Symbol('秘密')]: '我是隐藏的',
  normal: '我是普通的'
};

console.log(Object.keys(obj)); // 只输出 ['normal']
console.log('normal' in obj); // true
console.log('秘密' in obj); // false,因为要用Symbol访问


怎么创建和使用Symbol

创建Symbol

const sym1 = Symbol('id');
const sym2 = Symbol('id');

console.log(sym1 === sym2); // false
// 就像两个学生都叫"张三",但学号不同

作为对象属性

要注意访问方式:

// 这样访问不到
let user = {
  name: '张三',
  [Symbol('id')]: 123
};

console.log(user[Symbol('id')]); // undefined
// 每次Symbol('id')都是新的,所以访问不到

正确做法是保存Symbol引用:

const idSymbol = Symbol('id');

let user = {
  name: '张三',
  [idSymbol]: 123
};

console.log(user[idSymbol]); // 123

全局Symbol

如果你想要一个Symbol能在不同地方使用,可以用Symbol.for():

// 从全局注册表读取,没有就创建
const sym1 = Symbol.for('app.id');
const sym2 = Symbol.for('app.id');

console.log(sym1 === sym2); // true
// 这是同一个Symbol

Symbol.for()和Symbol()的区别:

  • Symbol()每次创建新的

  • Symbol.for()先检查全局有没有,有就返回,没有才创建

// 获取Symbol的描述
const sym = Symbol.for('app.id');
console.log(Symbol.keyFor(sym)); // 'app.id'


内置Symbol

JavaScript提供了一些内置的Symbol,可以改变对象的默认行为。

内置Symbol作用
Symbol.iterator让对象可以用for...of循环
Symbol.toStringTag自定义对象的toString输出
Symbol.hasInstance自定义instanceof的行为
Symbol.toPrimitive对象转原始值时调用
Symbol.match被字符串的match方法调用
Symbol.replace被字符串的replace方法调用
Symbol.search被字符串的search方法调用

Symbol.iterator示例

让对象可以循环:

const myNumbers = {
  [Symbol.iterator]: function* () {
    yield 1;
    yield 2;
    yield 3;
  }
};

for (const num of myNumbers) {
  console.log(num); // 依次输出:1, 2, 3
}

// 也可以用扩展运算符
console.log([...myNumbers]); // [1, 2, 3]

function*是生成器函数,yield是产生值的关键字。

Symbol.toStringTag示例

自定义对象类型显示:

class MyCollection {
  constructor() {
    this.items = [];
  }
  
  get [Symbol.toStringTag]() {
    return 'MyCollection';
  }
}

const coll = new MyCollection();
console.log(Object.prototype.toString.call(coll)); 
// 输出:[object MyCollection]

Symbol.hasInstance示例

自定义instanceof行为:

class MyArray {
  static [Symbol.hasInstance](instance) {
    return Array.isArray(instance);
  }
}

console.log([] instanceof MyArray); // true
console.log({} instanceof MyArray); // false


实际应用场景

1. 避免属性名冲突

当多个库或模块操作同一个对象时,用Symbol可以避免覆盖已有属性。

// 模块A
const MODULE_A_KEY = Symbol('module_a');
const data = {};

// 模块A设置数据
data[MODULE_A_KEY] = '模块A的数据';

// 模块B
const MODULE_B_KEY = Symbol('module_b');
// 模块B也设置数据,不会覆盖模块A的
data[MODULE_B_KEY] = '模块B的数据';

console.log(data[MODULE_A_KEY]); // '模块A的数据'
console.log(data[MODULE_B_KEY]); // '模块B的数据'

2. 模拟私有属性

虽然不是真正的私有,但可以隐藏属性:

const _password = Symbol('password');

class User {
  constructor(name, password) {
    this.name = name;
    this[_password] = password;
  }
  
  checkPassword(inputPassword) {
    return this[_password] === inputPassword;
  }
}

const user = new User('李四', 'mypassword123');

console.log(user.name); // '李四'
console.log(user[_password]); // 'mypassword123'(需要知道Symbol才能访问)
console.log(Object.keys(user)); // ['name'](常规遍历看不到password)

3. 定义常量

用Symbol定义一组唯一的值:

const LOG_LEVEL = {
  DEBUG: Symbol('debug'),
  INFO: Symbol('info'),
  WARN: Symbol('warn'),
  ERROR: Symbol('error')
};

function log(message, level = LOG_LEVEL.INFO) {
  switch(level) {
    case LOG_LEVEL.DEBUG:
      console.debug('调试:', message);
      break;
    case LOG_LEVEL.INFO:
      console.info('信息:', message);
      break;
    case LOG_LEVEL.WARN:
      console.warn('警告:', message);
      break;
    case LOG_LEVEL.ERROR:
      console.error('错误:', message);
      break;
  }
}

log('系统启动', LOG_LEVEL.INFO);
log('找不到文件', LOG_LEVEL.ERROR);

这样定义的常量值不会重复,比用字符串更安全。

4. 元编程

修改对象的内置行为:

const temperature = {
  value: 25,
  unit: '°C',
  
  [Symbol.toPrimitive](hint) {
    // hint表示转换类型:'string'、'number'或'default'
    if (hint === 'string') {
      return `${this.value}${this.unit}`;
    }
    if (hint === 'number') {
      return this.value;
    }
    return this.value.toString();
  }
};

console.log(String(temperature)); // "25°C"
console.log(Number(temperature)); // 25
console.log(temperature + 5); // 30(转数字后相加)
console.log('温度是' + temperature); // "温度是25°C"


注意事项

类型转换

const sym = Symbol('测试');

// 可以转字符串
console.log(String(sym)); // "Symbol(测试)"
console.log(sym.toString()); // "Symbol(测试)"

// 可以转布尔值
console.log(Boolean(sym)); // true
console.log(!sym); // false

// 不能转数字
console.log(Number(sym)); // 报错:TypeError

// 不能和字符串拼接(直接)
console.log('符号:' + sym); // 报错:TypeError
console.log(`符号:${String(sym)}`); // 正确:"符号:Symbol(测试)"

属性遍历

const obj = {
  normal: '普通属性',
  [Symbol('sym1')]: 'Symbol属性1',
  [Symbol('sym2')]: 'Symbol属性2'
};

// 只获取普通属性
console.log(Object.keys(obj)); // ['normal']
console.log(Object.getOwnPropertyNames(obj)); // ['normal']

// 只获取Symbol属性
console.log(Object.getOwnPropertySymbols(obj)); 
// [Symbol(sym1), Symbol(sym2)]

// 获取所有属性(包括Symbol)
console.log(Reflect.ownKeys(obj)); 
// ['normal', Symbol(sym1), Symbol(sym2)]


使用建议

  1. 需要唯一标识时用Symbol:比如做对象属性名,避免冲突。

  2. 需要隐藏属性时用Symbol:虽然不是真正私有,但能减少意外访问。

  3. 跨模块共享用Symbol.for():需要在不同地方使用同一个Symbol时。

  4. 修改对象行为用内置Symbol:比如让对象可迭代、自定义类型显示等。

  5. 注意性能:Symbol操作比字符串稍慢,在性能要求高的地方要考虑。


常见问题

Q:Symbol是对象吗?
A:不是。Symbol是原始类型,不能new Symbol()。

Q:Symbol能序列化吗?
A:不能。JSON.stringify会忽略Symbol属性。

Q:怎么判断一个值是Symbol?
A:用typeof:

const sym = Symbol();
console.log(typeof sym); // "symbol"

Q:Symbol能用作Map的键吗?
A:可以。Symbol可以用作Map和Set的键。

const map = new Map();
const sym = Symbol('key');
map.set(sym, 'value');
console.log(map.get(sym)); // 'value'


总结

Symbol是JavaScript中表示唯一值的原始类型。主要特点:

  • 每个Symbol都是唯一的

  • 适合做对象属性名,避免冲突

  • 不会出现在常规遍历中

  • 内置Symbol可以改变对象行为

使用场景:

  • 避免属性名冲突(特别是库和框架开发)

  • 模拟私有属性

  • 定义唯一常量

  • 元编程(修改对象行为)

Symbol让JavaScript更强大,特别是在大型项目和框架开发中很有用。虽然开始可能不习惯,但用好了能让代码更清晰、更安全。

记住关键点:Symbol的唯一性是最重要的特性。正是因为这个特性,它才能解决属性名冲突的问题。

本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://fly63.com/article/detial/13305

用JavaScript带你体验V8引擎解析标识符

先上知识点,标识符的扫描分为快解析和慢解析,一旦出现Ascii编码大于128的字符或者转义字符,会进入慢解析,略微影响性能,所以最好不要用中文、特殊字符来做变量名(不过现在代码压缩后基本不会有这种情况了)。

JS几种生成唯一id的方法

在开发中偶尔会遇到需要生成唯一id的时候,比如对数组的任意一项进行多次增删改,这时就需要给每一项添加唯一标识符来加以区分。以下便是从网络中搜集而来的各种生成唯一标识的方法,在此总结以供以后查阅。

js生成唯一标识_uuid/guid生成

如何创建GUID/UUIDUUID(通用唯一标识符),也称为 GUID(全局唯一标识符),根据RFC 4122,是旨在提供某些唯一性保证的标识符。

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!