你应该如何继承节点中的EventEmitter?

时间:2013-11-25 18:58:58

标签: javascript node.js events constructor prototype

我正在阅读this small article以了解从EventEmitter继承的内容,但我有点困惑。

他这样做:

function Door() {
    events.EventEmitter.call(this);
    this.open = function() {
        this.emit('open');
    };
}
Door.prototype.__proto__ = events.EventEmitter.prototype;

https://gist.github.com/chevex/7646362

为什么他用自己的构造函数this手动调用EventEmitter构造函数?另外,为什么他将他的contsructor原型的原型设置为EventEmitter的原型?这让我感到非常困惑。

然后评论中有人建议他这样做,这似乎更优雅:

function Door() {
    events.EventEmitter.call(this);
    this.open = function () {
      this.emit('open');
    }
}
util.inherits(Door, events.EventEmitter);

https://gist.github.com/chevex/7646447

这似乎比其他方式干净,但这可能只是因为我无法理解第一次发生的事情。如果util.inherits与第一个例子做同样的事情,我不会感到惊讶。

第二个对我来说至少有点意义,但我仍然不明白他们为什么不这样做:

function Door() {
    this.open = function () {
      this.emit('open');
    }
}
Door.prototype = new events.EventEmitter();

https://gist.github.com/chevex/7646524

任何人都可以向我解释所有这些方法之间的区别是什么以及为什么在前两个方法中它们在.call(this)构造函数上调用EventEmitter?我在尝试示例时省略了这一行,但仍然有效。

4 个答案:

答案 0 :(得分:9)

第三个示例 not 通常是正确的:为所有门实例创建一个EventEmitter个实例。

让我们想象一个简单的案例:

var Foo = function() {
    // each Foo instance has a unique id
    this.id = Math.random();
}
Foo.prototype.doFoo = function() { console.log("Foo!"); }

假设我们要创建一个继承自Bar的{​​{1}}构造函数,并添加一些新属性。如果你按照你的最后一个例子:

Foo

这是错误的,因为所有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设置为__proto__参数。

答案 1 :(得分:3)

  

为什么他用自己的构造函数手动调用EventEmitter构造函数呢?

这是确保执行EventEmitter构造函数中的任何代码所必需的。有些类可能在构造函数中没有做任何有趣的事情,但是其他类在那里会有重要的代码,所以你应该总是这样做以确保代码的运行方式与你刚刚用{{1创建一个新的EventEmitter时的运行方式相同}}

在某些语言(例如Java)中,这种“构造函数链接”类型行为是隐式的,但在JavaScript中它必须明确地完成。

究竟如何在JavaScript中设置继承归结为“它很复杂”的答案,而其他人可能比I更加公正。还有一些可行的变化,人们不同,哪个更好。但是,source for util.inherits是{FYI}在这里,对我所见过的所有风味进行最深入的检查是在这个视频中:Douglas Crockford: Advanced JavaScript。基本上,要定期观察它,直到它沉入其中。

寻找参考的地方。如果您完全理解所有这些是如何工作的,那么您已经掌握了它(它仍然会在整个过程中将我的大脑变成椒盐脆饼)

答案 2 :(得分:3)

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); 属性。

function Door() {
}
Door.prototype = new events.EventEmitter();

这是在节点中继承的惯用方法。所以你最好使用它。但它的作用与上述基本相同。

{{1}}

这是错误的方式,不要使用它!最终将在某些版本的节点中的实例之间共享事件。

答案 3 :(得分:2)

Node v6.3.1文档说明了util.inherits(constructor, superConstructor)

  

不建议使用util.inherits()。请使用ES6 classextends关键字获取语言级继承支持。另请注意,这两种样式在语义上不兼容。

以下代码显示了如何使用Typescript继承EventEmitter

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'");