symbol作为对象属性

93 阅读2分钟

一、基础概念

1. 定义与特性

  • 唯一性:Symbol 是 ES6 新增的原始数据类型,通过 Symbol() 函数创建,每个 Symbol 值都是唯一的。

    const s1 = Symbol('key');
    const s2 = Symbol('key');
    s1 === s2; // false
    
  • 不可枚举性:默认不会出现在 for...inObject.keys() 中,常用于创建对象私有属性。

    const obj = { [Symbol('private')]: 'value' };
    Object.keys(obj); // []
    

2. 语法

// 创建 Symbol
const sym = Symbol('description'); // description 仅用于调试,不影响唯一性

// 作为对象属性
const obj = {
  [sym]: 'data',
};

二、核心应用场景

1. 作为对象属性键(避免命名冲突)

const NAME = Symbol('name');
class Person {
  constructor() {
    this[NAME] = '张三';
  }
  getName() {
    return this[NAME];
  }
}

2. 定义私有属性/方法

const PRIVATE_METHOD = Symbol();
class MyClass {
  [PRIVATE_METHOD]() { /* 私有逻辑 */ }
  publicMethod() {
    this[PRIVATE_METHOD](); // 内部可访问
  }
}

3. 实现自定义迭代器

const obj = {
  [Symbol.iterator]: function*() {
    yield 1;
    yield 2;
  }
};
[...obj]; // [1, 2]

4. 模拟枚举类型

const DIRECTION = {
  UP: Symbol('up'),
  DOWN: Symbol('down'),
};

三、内置 Symbol(元编程)

ES6 提供了一些内置的 Symbol,用于修改对象的默认行为:

内置 Symbol作用示例
Symbol.iterator定义对象的迭代器方法obj[Symbol.iterator] = function() { ... }
Symbol.toStringTag自定义 Object.prototype.toString() 的返回值obj[Symbol.toStringTag] = 'MyClass'
Symbol.toPrimitive定义对象转换为原始值的逻辑obj[Symbol.toPrimitive] = hint => ...
Symbol.species指定创建衍生对象时使用的构造函数class MyArray extends Array { static get [Symbol.species]() { ... } }
Symbol.hasInstance自定义 instanceof 行为class MyClass { static [Symbol.hasInstance](obj) { ... } }

四、问题

1. 问:Symbol 为什么适合作为对象属性?

 - **唯一性**:避免属性名冲突(如混入多个第三方库时)。  
 - **不可枚举性**:默认不会被遍历,适合隐藏实现细节。  

2. 问:如何获取对象的 Symbol 属性?

 ```javascript
 const sym = Symbol('key');
 const obj = { [sym]: 'value' };
 
 // 方法1:使用 Object.getOwnPropertySymbols()
 Object.getOwnPropertySymbols(obj); // [Symbol(key)]
 
 // 方法2:使用 Reflect.ownKeys()(获取所有键,包括 Symbol)
 Reflect.ownKeys(obj); // [Symbol(key)]
 ```  

3. 问:Symbol 与 WeakMap 结合实现私有属性的优势?

 ```javascript
 const privateData = new WeakMap();
 class Person {
   constructor() {
     privateData.set(this, { age: 18 });
   }
   getAge() {
     return privateData.get(this).age;
   }
 }
 ```  
 - **优势**:  
   - 真正私有(外部无法访问 `privateData`)。  
   - 垃圾回收友好(对象被销毁时,WeakMap 中的引用自动释放)。  

4. 问:Symbol 能被序列化吗?

 ```javascript
 const obj = { [Symbol('key')]: 'value' };
 JSON.stringify(obj); // {}(Symbol 属性会被忽略)
 ```  
 - **原因**:Symbol 是唯一且不可预测的,序列化后无法保证唯一性。