你应该如何inheritanceEventEmitter节点?
我正在阅读这篇小文章,以了解从EventEmitter
inheritance,但我有点困惑。
他这样做:
function Door() { events.EventEmitter.call(this); this.open = function() { this.emit('open'); }; } Door.prototype.__proto__ = events.EventEmitter.prototype;
https://gist.github.com/chevex/7646362
为什么他用自己的构造函数手动调用EventEmitter的构造函数呢? 另外,为什么他将原型的原型设置为EventEmitter的原型呢? 这对我来说非常混乱。
然后在评论中有人build议他这样做,而这看起来更优雅:
function Door() { events.EventEmitter.call(this); this.open = function () { this.emit('open'); } } util.inherits(Door, events.EventEmitter);
https://gist.github.com/chevex/7646447
这似乎比其他方式更干净,尽pipe这可能只是因为我不明白发生了什么事情。 如果util.inherits
和第一个例子一样,我也不会感到惊讶。
第二个至less对我有点意义,但是我还是不明白为什么他们不这样做:
function Door() { this.open = function () { this.emit('open'); } } Door.prototype = new events.EventEmitter();
https://gist.github.com/chevex/7646524
任何人都可以向我解释所有这些方法之间的区别是什么,为什么在前两个他们调用EventEmitter
构造函数EventEmitter
.call(this)
? 我在尝试示例时忽略了这一行,他们仍然工作。
第三个示例通常不正确:为所有门实例创build一个EventEmitter
实例。
让我们设想一个简单的例子:
var Foo = function() { // each Foo instance has a unique id this.id = Math.random(); } Foo.prototype.doFoo = function() { console.log("Foo!"); }
假设我们要创build一个从Foo
inheritance的Bar
构造函数并添加一些新的属性。 如果你遵循你的最后一个例子:
var Bar = function() { this.something = 5; } Bar.prototype = new Foo();
这是错误的,因为所有Bar
实例将具有相同的id
属性。 相反,我们必须为每个实例调用父构造函数:
var Bar = function() { Foo.call(this); // set unique `id` on `this` this.something = 5; } Bar.prototype = Object.create(Foo.prototype);
请注意,这里的最后一行与Bar.prototype.__proto__ = Foo.prototype;
因为Object.create
创build一个新的对象,其__proto__
被设置为Object.create
参数。
为什么他用自己的构造函数手动调用EventEmitter的构造函数呢?
这是确保在EventEmitter
构造函数中执行任何代码所必需的。 有些类可能在构造函数中没有做任何有趣的事情,但是其他类将会有重要的代码,所以你应该总是这样做,以确保代码的运行方式与运行新的EventEmitter的方式一样,直接使用var emitter = new EventEmitter;
在某些语言(比如Java)中,这个“构造器链接”types的行为是隐含的,但是在JavaScript中必须明确地完成。
究竟如何设置JavaScript的inheritance归结为“这是复杂”的答案,其他人可能会做得比我更公正。还有几个可行的变化和人们的不同之处是可取的。 然而,仅供参考util.inherits的来源是在这里,最好深入的审查这个我看过的所有口味是在这个video: 道格拉斯Crockford:先进的JavaScript 。 基本上,定期观察,直到它陷入。
寻找参考的地方。 如果你完全理解这些是如何工作的,那么你已经掌握了它(它仍然把我的大脑变成一个椒盐卷饼)
- Backbone的扩展帮助函数 (Backbone.Model.extend,Backbone.View.extend等)
- CoffeeScript的类支持
- node.js的util.extend
function Door() { events.EventEmitter.call(this); } Door.prototype.__proto__ = events.EventEmitter.prototype;
在这种情况下,使用events.EventEmitter.call(this)
就像在使用super
的语言中一样。 实际上在简单的情况下可以省略,但是会破坏当前节点版本的domain
支持,也许在将来的版本中会有其他问题。
设置__proto__
只是设置了原型链。 也可以这样做:
Door.prototype = Object.create(events.EventEmitter.prototype);
但在这种情况下,您还需要手动设置constructor
属性。
util.inherits(Door, events.EventEmitter);
这是inheritance节点的惯用方式。 所以你最好用这个。 但是它所做的基本上和上面一样。
function Door() { } Door.prototype = new events.EventEmitter();
这是错误的方式,不要使用它! 您将以在一些节点版本中的实例之间共享事件而结束。
Node v6.3.1文档指出了util.inherits(constructor, superConstructor)
:
不鼓励使用
util.inherits()
。 请使用ES6class
并extends
关键字以获得语言级别的inheritance支持。 另请注意,这两种风格在语义上是不兼容的。
以下代码显示了如何使用Typescript从EventEmitter
inheritance:
import { EventEmitter } from "events" class Person extends EventEmitter { constructor(public name: string) { super() } } let person = new Person("Bob") person.on("speak", function (said: string) { console.log(`${this.name} said: ${said}`) }) person.emit("speak", "'hello'") // prints "Bob said: 'hello'"
以前的代码将转换成以下ES6代码:
"use strict"; const events = require("events"); class Person extends events.EventEmitter { constructor(name) { super(); this.name = name; } } let person = new Person("Bob"); person.on("speak", function (said) { console.log(`${this.name} said: ${said}`); }); person.emit("speak", "'hello'");