当前位置: 首页 > article >正文

es6中关于symbol的用法,以及使用场景

1. Symbol 基本概念

1.1 创建 Symbol

// 基本创建方式
const s1 = Symbol();
const s2 = Symbol('description');

// 每个 Symbol 都是唯一的
console.log(Symbol() === Symbol()); // false
console.log(Symbol('key') === Symbol('key')); // false

1.2 Symbol.for() 和 Symbol.keyFor()

// 使用 Symbol.for() 创建共享的 Symbol
const s1 = Symbol.for('shared');
const s2 = Symbol.for('shared');
console.log(s1 === s2); // true

// 使用 Symbol.keyFor() 获取共享 Symbol 的键
console.log(Symbol.keyFor(s1)); // 'shared'
console.log(Symbol.keyFor(Symbol('not shared'))); // undefined

2. Symbol 作为对象属性

2.1 基本用法

const mySymbol = Symbol('mySymbol');

// 作为对象的属性键
const obj = {
  [mySymbol]: 'Hello Symbol'
};

// 访问 Symbol 属性
console.log(obj[mySymbol]); // 'Hello Symbol'

// 添加 Symbol 属性
obj[Symbol('anotherSymbol')] = 'Another value';

2.2 Symbol 属性的特点

const obj = {
  [Symbol('name')]: 'John',
  age: 25
};

// Symbol 属性不会出现在常规的属性枚举中
console.log(Object.keys(obj)); // ['age']
console.log(Object.getOwnPropertyNames(obj)); // ['age']

// 获取 Symbol 属性
console.log(Object.getOwnPropertySymbols(obj)); // [Symbol(name)]

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

3. 内置 Symbol 值

3.1 Symbol.iterator 迭代器

// 使对象可迭代
const collection = {
  items: ['item1', 'item2', 'item3'],
  [Symbol.iterator]() {
    let index = 0;
    return {
      next: () => {
        return {
          done: index >= this.items.length,
          value: this.items[index++]
        };
      }
    };
  }
};

// 现在可以使用 for...of 循环
for (const item of collection) {
  console.log(item);
}

3.2 Symbol.toStringTag

class CustomClass {
  get [Symbol.toStringTag]() {
    return 'CustomClass';
  }
}

const obj = new CustomClass();
console.log(Object.prototype.toString.call(obj)); // '[object CustomClass]'

3.3 Symbol.species

class MyArray extends Array {
  static get [Symbol.species]() {
    return Array; // 指定派生对象的构造函数
  }
}

const arr = new MyArray(1, 2, 3);
const mapped = arr.map(x => x * 2); // 返回 Array 实例,而不是 MyArray

4. 实际应用场景

4.1 私有属性模拟

const privateProperty = Symbol('privateProperty');
const privateMethod = Symbol('privateMethod');

class MyClass {
  constructor() {
    this[privateProperty] = 'private value';
  }

  [privateMethod]() {
    return 'private method';
  }

  publicMethod() {
    return this[privateMethod]();
  }
}

const instance = new MyClass();
console.log(instance[privateProperty]); // undefined(如果不知道 Symbol)

4.2 防止属性名冲突

// 在库或框架中使用
const librarySymbol = Symbol('myLibrary');

class ThirdPartyClass {
  constructor() {
    this[librarySymbol] = {
      // 库的私有数据
      config: {},
      cache: new Map()
    };
  }
}

4.3 实现特定的接口或协议

const protocol = {
  connect: Symbol('connect'),
  disconnect: Symbol('disconnect')
};

class Device {
  [protocol.connect]() {
    console.log('Device connected');
  }

  [protocol.disconnect]() {
    console.log('Device disconnected');
  }
}

5. Symbol 与元编程

5.1 自定义对象的基本行为

const myObject = {
  [Symbol.toPrimitive](hint) {
    switch (hint) {
      case 'number':
        return 42;
      case 'string':
        return 'custom string';
      default:
        return 'default';
    }
  }
};

console.log(+myObject); // 42
console.log(\`\${myObject}\`); // 'custom string'

5.2 自定义对象的检查行为

const customInspect = Symbol.for('nodejs.util.inspect.custom');

class CustomObject {
  [customInspect]() {
    return 'CustomObject(...)';
  }
}

// 在 Node.js 环境中使用 console.log 时的自定义输出
console.log(new CustomObject());

6. 最佳实践

6.1 使用建议

// ✅ 好的实践
// 1. 使用描述性的 Symbol 描述
const userToken = Symbol('user authentication token');

// 2. 用于常量值
const STATUS = {
  PENDING: Symbol('pending'),
  FULFILLED: Symbol('fulfilled'),
  REJECTED: Symbol('rejected')
};

// 3. 用于私有属性
const _data = Symbol('private data');
class MyClass {
  constructor() {
    this[_data] = {};
  }
}

6.2 注意事项

// 1. Symbol 不能使用 new 操作符
const sym = new Symbol(); // TypeError

// 2. Symbol 不能隐式转换为字符串
const sym = Symbol('my symbol');
alert(sym); // TypeError

// 正确的字符串转换
alert(sym.toString()); // 'Symbol(my symbol)'
alert(sym.description); // 'my symbol'

// 3. JSON 序列化时会忽略 Symbol 属性
const obj = {
  [Symbol('id')]: 1,
  name: 'John'
};
console.log(JSON.stringify(obj)); // '{"name":"John"}'

7. Symbol 的遍历方法

7.1 不同遍历方法的对比

const obj = {
  name: 'John',
  age: 25,
  [Symbol('id')]: 1,
  [Symbol('key')]: 'value'
};

// 1. Object.keys() - 只返回常规属性名
console.log(Object.keys(obj));  
// ['name', 'age']

// 2. Object.getOwnPropertyNames() - 只返回常规属性名
console.log(Object.getOwnPropertyNames(obj));  
// ['name', 'age']

// 3. Object.getOwnPropertySymbols() - 只返回 Symbol 属性
console.log(Object.getOwnPropertySymbols(obj));  
// [Symbol(id), Symbol(key)]

// 4. Reflect.ownKeys() - 返回所有属性,包括 Symbol
console.log(Reflect.ownKeys(obj));  
// ['name', 'age', Symbol(id), Symbol(key)]

7.2 实际应用示例

7.2.1 获取对象的所有 Symbol 属性
function getAllSymbolProperties(obj) {
  return Object.getOwnPropertySymbols(obj)
    .reduce((result, symbol) => {
      result[symbol.description] = obj[symbol];
      return result;
    }, {});
}

const user = {
  name: 'John',
  [Symbol('id')]: 123,
  [Symbol('role')]: 'admin'
};

console.log(getAllSymbolProperties(user));
// { id: 123, role: 'admin' }
7.2.2 合并包含 Symbol 的对象
function mergeWithSymbols(target, ...sources) {
  sources.forEach(source => {
    // 合并常规属性
    Object.assign(target, source);
    
    // 合并 Symbol 属性
    Object.getOwnPropertySymbols(source).forEach(symbol => {
      target[symbol] = source[symbol];
    });
  });
  
  return target;
}

const baseConfig = {
  name: 'config',
  [Symbol('secret')]: 'xyz'
};

const extraConfig = {
  version: '1.0',
  [Symbol('key')]: '123'
};

const finalConfig = mergeWithSymbols({}, baseConfig, extraConfig);
console.log(Reflect.ownKeys(finalConfig));
// ['name', 'version', Symbol(secret), Symbol(key)]
7.2.3 深度克隆包含 Symbol 的对象
function deepCloneWithSymbols(obj) {
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }

  const clone = Array.isArray(obj) ? [] : {};

  // 克隆常规属性和 Symbol 属性
  Reflect.ownKeys(obj).forEach(key => {
    clone[key] = deepCloneWithSymbols(obj[key]);
  });

  return clone;
}

const original = {
  name: 'test',
  [Symbol('id')]: 1,
  nested: {
    [Symbol('key')]: 'value'
  }
};

const cloned = deepCloneWithSymbols(original);
console.log(
  Object.getOwnPropertySymbols(cloned).length === 
  Object.getOwnPropertySymbols(original).length
); // true

7.3 遍历方法的使用建议

// ✅ 好的实践

// 1. 当需要处理所有属性时使用 Reflect.ownKeys()
function processAllProperties(obj) {
  Reflect.ownKeys(obj).forEach(key => {
    // 处理所有属性,包括 Symbol
    console.log(key, obj[key]);
  });
}

// 2. 当只需要处理 Symbol 属性时使用 Object.getOwnPropertySymbols()
function processSymbolsOnly(obj) {
  Object.getOwnPropertySymbols(obj).forEach(symbol => {
    // 只处理 Symbol 属性
    console.log(symbol.description, obj[symbol]);
  });
}

// 3. 分别处理常规属性和 Symbol 属性
function processPropertiesSeparately(obj) {
  // 处理常规属性
  Object.keys(obj).forEach(key => {
    console.log('Regular:', key, obj[key]);
  });
  
  // 处理 Symbol 属性
  Object.getOwnPropertySymbols(obj).forEach(symbol => {
    console.log('Symbol:', symbol.description, obj[symbol]);
  });
}

7.4 性能考虑

// 在处理大对象时的优化建议
function optimizedPropertyProcessing(obj) {
  // 1. 缓存 Symbol 属性
  const symbols = Object.getOwnPropertySymbols(obj);
  if (symbols.length > 0) {
    const symbolProps = {};
    symbols.forEach(symbol => {
      symbolProps[symbol.description] = obj[symbol];
    });
  }

  // 2. 分批处理常规属性
  const regularKeys = Object.keys(obj);
  const batchSize = 1000;
  for (let i = 0; i < regularKeys.length; i += batchSize) {
    const batch = regularKeys.slice(i, i + batchSize);
    // 处理这一批属性
    batch.forEach(key => {
      // 处理逻辑
    });
  }
}

http://www.kler.cn/a/537221.html

相关文章:

  • (10) 如何获取 linux 系统上的 TCP 、 UDP 套接字的收发缓存的默认大小,以及代码范例
  • flappy-bird-gymnasium
  • 问卷数据分析|SPSS之分类变量描述性统计
  • Python爬虫-如何正确解决起点中文网的无限debugger
  • Safari常用快捷键
  • AllData数据中台核心菜单十二:数据同步平台
  • Kotlin 2.1.0 入门教程(十)
  • TAPEX:通过神经SQL执行器学习的表格预训练
  • Ubuntu20.04 本地部署 DeepSeek-R1 及 chatbox可视化
  • TCN时间卷积神经网络多变量多步光伏功率预测(Matlab)
  • 4-redis分片集群
  • springboot配置redis
  • RTOS基础(TODO)
  • Jupyter Notebook使用指南--虚拟环境
  • 使用scoop 下载速度慢怎么办
  • Day38【AI思考】-彻底打通线性数据结构间的血脉联系
  • 位置定位与IP属地:异同解析与实际应用
  • ios应用想要下载到手机上只能苹果签名吗
  • IDEA+DeepSeek让Java开发起飞
  • go的sync包学习
  • 数据库性能优化(sql优化)_统计信息_yxy
  • 深入理解Docker:为你的爬虫项目提供隔离环境
  • C#+halcon机器视觉九点标定算法
  • 利用 Python 爬虫获取按关键字搜索淘宝商品的完整指南
  • 初探DeepSeek
  • PbootCMS 修改跳转提示,修改笑脸时间