- Welcome to the Node.js Platform
- Node.js Essential Patterns
- Asynchronous Control Flow Patterns with Callbacks
- Asynchronous Control Flow Patterns with ES2015 and Beyond
- Coding with Streams
- Design Patterns
- Writing Modules
- Advanced Asynchronous Recipes
- Scalability and Architectural Patterns
- Messaging and Integration Patterns
- Welcome to the Node.js Platform
- Node.js 的发展
- Node.js 的特点
- 介绍 Node.js 6 和 ES2015 的新语法
- reactor 模式
- Node.js Essential Patterns
- Asynchronous Control Flow Patterns with Callbacks
- Asynchronous Control Flow Patterns with ES2015 and Beyond
- Coding with Streams
- Design Patterns
- Writing Modules
- Advanced Asynchronous Recipes
- Scalability and Architectural Patterns
- Messaging and Integration Patterns
揭示构造函数模式( Revealing constructor )
揭示构造函数模式是一个相对较新的模式,在 Node.js
社区和 JavaScript
中越来越受到重视,特别是因为它在一些核心库(如 Promise
)中使用。
我们已经在 Chapter4-Asynchronous Control Flow Patterns with ES2015 and Beyond
中隐含地看到了这种模式,但是我们再回过头来分析一下 Promise
构造函数,以更详细地描述它:
const promise = new Promise(function(resolve, reject) {
// ...
});
正如你所看到的, Promise
接受一个函数作为构造函数的参数,这被称为执行函数。这个函数是由 Promise
构造函数的内部实现调用的,它提供给构造函数,用于处理 pending
状态的 promise
的内部状态。换句话说,它确定了一个方式来调用 resolve
和 reject
函数, promise
遵循这个机制,调用 resolve
和 reject
来改变对象的内部状态。
这样做的好处是只有构造函数的参数函数才有权 resolve
和 reject
,一旦构造了 Promise
对象,就可以安全地传递;没有其他代码将能够调用 resolve
或 ject
,来改变 Promise
的内部状态。 这就是为什么这个模式被 Domenic Denicola
的一篇博客文章命名为揭示构造函数模式的原因。
一个只读的 event emitter
在这一段中,我们将使用揭示构造函数模式来构建一个只读的 event emitter
,这是一种特殊类型的 event emitter
,在这个 event emitter
内部方法,不允许调用 emit
方法,只有传递给构造函数的函数参数才能够调用 emit
方法。
让我们将 Roee
类的代码写入名为 roee.js
的文件中:
const EventEmitter = require('events');
module.exports = class Roee extends EventEmitter {
constructor(executor) {
super();
const emit = this.emit.bind(this);
this.emit = undefined;
executor(emit);
}
};
在这个简单的类中,我们扩展了核心模块 EventEmitter
类,其接受一个 executor
函数作为构造函数的唯一参数。
在构造函数内部,我们调用 super
函数来确保通过调用其父构造函数来正确地初始化 event emitter
,然后保存 emit
函数的备份,并通过为其分配 undefined
来删除它。
最后,我们通过传递 emit
方法备份作为参数来调用 executor
函数。
这里要了解的重要一点是,在 undefined
被分配给 emit
方法之后,我们不能再从代码的其他部分调用它了。 我们的 emit
的备份版本被定义为一个局部变量,只会被转发给执行器函数。这个机制使我们能够仅在 executor
函数内使用 emit
。
现在让我们使用这个新类来创建一个简单的 ticker
,一个每秒发出一个 tick
并记录所有 tick
发出的数量的类。
这将是我们新的 ticker.js
模块的内容:
const Roee = require('./roee');
const ticker = new Roee((emit) => {
let tickCount = 0;
setInterval(() => emit('tick', tickCount++), 1000);
});
module.exports = ticker;
正如你在这里看到的,代码量并不大。 我们实例化一个新的 Roee
,并在 executor
函数内传递 emit
作为参数。正是因为我们的 executor
函数接收 emit
作为参数,所以我们可以使用它每秒发出一个新的 tick
事件。
现在我们举例说明如何使用这个模块:
const ticker = require('./ticker');
ticker.on('tick', (tickCount) => console.log(tickCount, 'TICK'));
// ticker.emit('something', {}); <-- This will fail
我们使用与任何其他基于 event emitter
的对象相同的 ticker
对象,我们可以用 on
方法附加任意数量的监听器,但是在这种情况下,如果我们尝试使用 emit
方法,那么我们的代码将抛出异常 TypeError: ticker.emit is not a function
。
即使这个例子在展示如何使用揭示构造函数模式,但值得一提的是这个事件发生器的只读功能并不是完美的,并且仍然有可能以几种方式绕过它。例如,我们仍然可以通过直接使用原型上的
emit
在我们的ticker
实例上发出事件,如下所示:
require('events').prototype.emit.call(ticker, 'someEvent', {});
实际应用场景
即使这种模式非常有趣和智能,但实际上,除了 Promise
构造函数以外,很难找到常见的应用实例。
值得一提的是,现在 Streams
议案中有一个新的规范,可以尝试使用揭示构造函数模式替代现今的模板模式,以便能够描述各种 Streams
对象的行为:可以看 https://streams.spec.whatwg.org/
另外需要指出的是,在之前 Chapter 5-Coding with Streams
当我们实现了 ParallelStream
类的时候。这个类作为构造函数参数接受 userTransform
函数作为参数( executor
)。
即使在这种情况下, executor
函数在构建时不被调用,但在 Streams
的内部 _transform()
方法中,揭示构造函数模式的一般概念仍然有效。实际上,这种方法允许我们在创建一个新的 ParallelStream
实例时,将 Streams
的一些内部方法(例如 push
函数)暴露给 executor
函数,使得我们在调用构造函数创建 ParallelStream
实例时执行与内部方法相关的一些操作。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论