Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ES6 中的 symbol类 型在实际开发中用得多吗? #46

Open
qufei1993 opened this issue Oct 24, 2022 · 0 comments
Open

ES6 中的 symbol类 型在实际开发中用得多吗? #46

qufei1993 opened this issue Oct 24, 2022 · 0 comments
Assignees

Comments

@qufei1993
Copy link
Owner

知乎上看到的一个问题:“ES6 中的 symbol 类型在实际开发中用得多吗?”,以前在学习 ES6 时也有此疑问,这个 Symbol 是干嘛的?有什么应用场景?

Symbol 在实际开发中用的不多,在看一些类库的实现时有看到相关使用,下面分享下关于 Symbol 的几个使用场景。

做为私有属性

使用 Symbol 做为私有属性,最早知道这个是在 Egg 源码中学习到的,看一段相关的代码片段:

// https://github.com/eggjs/egg/blob/3.0.0/lib/core/base_context_logger.js#L22
const CALL = Symbol('BaseContextLogger#call');
class BaseContextLogger {
  [CALL](method, args) {
    // ...
    this.ctx.logger[method](...args);
  }
}

在 JavaScript 中为了实现私有属性,之前常用的一种方式是命名规范约定,方法名以 _ 开始

Symbol 出现之后看到的一个相对较多的场景是用它来模式私有属性、方法。这对一些 for...inObject.getOwnPropertyNames() 操作是可以隐藏掉这些属性,但是 ES6 中的 Symbol 和强类型语言中的 private 相比并不完全是私有的,仍然能通过 Object.getOwnPropertySymbols()Reflect.ownKeys() 操作枚举到这些属性进行访问。

钩子函数 - 自定义格式输出

使用 MongoDB Node.js 驱动程序生成一个 id,当执行 ObjectId()new ObjectId() 时总会按照固定格式输出,如下所示:

new ObjectId("632c6d93d65f74baeb22a2c9")

没了解实现之前,看起来总归是有些神秘的,如果自己写一个类并实现自定义输出信息该怎么做呢?带着好奇之心看了下源码实现:

// https://github.com/mongodb/js-bson/blob/v4.4.0/src/objectid.ts#L343
class ObjectId {
  toHexString(): string {
    const hexString = this.id.toString('hex');
    return hexString;
  }

  [Symbol.for('nodejs.util.inspect.custom')](): string {
    return this.inspect();
  }

  inspect(): string {
    return `new ObjectId("${this.toHexString()}")`;
  }
}

Symbol.for(str) 是新建一个以该字符串为名称的值,并注册到全局,如果已注册过,就直接返回。与 Symbol() 区别简单理解是,Symbol() 调用 100 次会返回 100 个不同 Symbol 值,Symbol.for(str) 调用 100 次返回的 Symbol 值都是相同的。

Node.js util 模块实现了 util.inspect.custom 方法用于声明自定义检查函数,这里个人理解更像一个钩子函数,在 nodejs/node#20821 PR 中已支持将 util.inspect.custom 做为公共符号,实现了不用加载 util 模块就可在任何地方使用它,这里用的就是 Symbol.for()。

面试官:实现一个可遍历对象

JavaScript 中的 Array、Set、Map 数据类型都可被 for...of 所遍历。如果面试官说:“实现一个可以被 for...of 所遍历的对象” 这个该怎么实现呢?

Symbol 提供了 Symbol.iterator 方法,该方法返回一个迭代器对象,目前 Array、Set、Map 这些数据结构默认具有 Symbol.iterator 属性,而对象 Object 是没有的,如下所示:

console.log([][Symbol.iterator]()); // Object [Array Iterator] {}
console.log((new Map())[Symbol.iterator]()); // [Map Entries] {  }
console.log((new Set())[Symbol.iterator]()); // [Set Iterator] {  }
console.log({}[Symbol.iterator]); // undefined 

Symbol.iterator 是迭代协议标准中的一部分:可迭代器协议,它定义了哪些值可以被遍历到。要成为可迭代器对象,必须实现 @@iterator 方法,可通过常量 Symbol.iterator 访问(到这里是不是发现,原来常使用的 Array 类型竟和 Symbol 也有联系啊

迭代协议标准的另一部分是:迭代器协议 ,它定义了产生一系列值的的标准方式。通过定义 next() 方法实现,这里不做详细阐述,参见文档

了解了 Symbol.iterator 和迭代协议规则实现一个可被遍历的对象并不难。

const range = {
  start: 0,
  end: 3,
  [Symbol.iterator]: function() { return this },
  next: function() {
    if (this.start > this.end) {
      return { value: undefined, done: true }
    }
  
    return { value: this.start++, done: false }
  }
}

for (const id of range) {
  console.log(id); // 0,1,2,3
}

除了 Symbol.iterator 还有 Symbol.asyncIterator,这个在 Node.js 后端中有一些使用场景。

Symbol 的详细介绍,推荐两个学习资源:

@qufei1993 qufei1993 self-assigned this Oct 24, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant