什么是事件发射器调用?

What is Event Emitter Call?

我正在学习 Node.js。我在我的书中遇到了一段代码,如下所示:

var EventEmitter = require("events").EventEmitter;
var inherits = require('util').inherits;

//Custom class
function Foo(){
   EventEmitter.call(this);
}
inherits(Foo, EventEmitter);

Foo.prototype.connect = function(){
    this.emit('connected');
}

var foo = new Foo();
foo.on('connected', function(){
    console.log("connected raised!');
}

foo.connect();

我的问题是"call"这里是做什么的?为什么 class Foo 继承自 EventEmitter?这是否意味着 Foo 是 Event Emitter 的子对象?如果是这样,它必须是 EventEmitter 的子项吗?我在 Whosebug 中发现了另一个关于调用的问题 (What does EventEmitter.call() do?) 但是,我不明白所提供的答案...谢谢

代码来源:开头 Node.js 作者:Basarat Ali Syed

代码行:

EventEmitter.call(this);

调用您正在继承的对象的构造函数,该构造函数允许 EventEmitter 代码初始化该对象的它的部分,这是 Javascript.

中继承过程的一部分

EventEmitter() 是 EventEmitter 对象的构造函数。由于您需要使用与新对象相同的 this 来调用该构造函数,因此您必须对该构造函数使用 .call().apply() 才能使正确的 this使用。由于您没有传递给构造函数的参数,因此 .call() 是调用它的最简单方法。

您必须调用 EventEmitter() 构造函数才能正确初始化使用 new Foo() 创建的对象部分。在 Javascript 中使用继承时,多个单独的对象定义使用同一个对象来存储它们的属性和方法,因此每个对象都初始化它们的对象部分,并且通过调用您从中继承的对象的构造函数开始初始化.

这是关于 chaining constructors 主题的一个很好的参考。


从您的一些评论看来,您不明白代码中的继承点是什么。该代码允许您创建一个对象类型 Foo,它具有您自己的方法,但该对象也是一个 eventEmitter 并且具有 EventEmitter 的所有功能,因此它可以触发事件、响应事件等。 .. 这称为 "inheritance" ,您可以在其中使用自己的自定义对象继承其他对象的功能。为了使继承起作用,您的代码做了两件事。使用 inherits(Foo, EventEmitter); 代码行,它继承了另一个对象的原型,因此它将具有所有可用的相同方法,使用 EventEmitter.call(this);,它调用继承对象的构造函数,以便对象可以初始化本身正确。

您可能想阅读几篇关于 Javascript 继承的参考文章:

Introduction to Object-Oriented JavaScript

Inheritance and the prototype chain

Understanding JavaScript Inheritance

What is "inheritance" in Javascript?

Inheritance: Object Oriented Programming

这是 ghetto JS 实现继承的方式,因为该语言只真正支持原型继承。没有像其他语言那样支持 class 继承的官方语言,但是我们可以 运行 针对不同上下文的函数这一事实已经成为在继承层次结构中硬塞的非常标准的 hack,其中您的 class 也可以被认为是其基础 class 的一个实例。也就是说,在var foo = new Foo()中,foo可以说是Foo的一个实例,也是EventEmitter的一个实例。在其他语言中,您可以设置语言和编译器支持的更明确的继承。

.call 适用于所有函数并允许您执行该函数,但具有不同的上下文。 Foo class 中的 this 指的是正在创建的 Foo 实例, EventEmitter.call(this); 是 运行 宁 EventEmitter 构造函数,但是在 EventEmitter 构造函数中使用 Foo 的实例作为 this 。这样,EventEmitter 构造函数通常通过 var emitter = new EventEmitter(); 在纯 EventEmitter 的新实例上设置的任何内容现在实际上都将在 您的 上设置Foo.

的实例

现在,这样只解决了实现JS伪继承目标的一半。如果 EventEmitter 原型上有任何我们需要在我们的 Foo 原型上出现的东西,仅仅调用 Foo 实例上的 EventEmitter 构造函数是不够的。这就是为什么您还必须调用 util.inherits(Foo, EventEmitter);.

util.inherits只是将Foo的原型设置为一个继承自EventEmitter原型的新对象。它还将 EventEmitter 构造函数作为 .super_ 属性 添加到您的 Foo 构造函数中,我认为这是一个 Java 约定,可以轻松访问基本构造函数(您继承自的 class 的构造函数)。 https://github.com/joyent/node/blob/master/lib/util.js#L634-L644

申请时:

inherits(Foo, EventEmitter);

Foo的原型设置为EventEmitter.prototype。因此,Foo 的每个实例都将拥有 EventEmitter 的所有方法(例如 onemit 等)

申请时:

EventEmitter.call(this)

Foo 的构造函数中,它与调用 new EventEmitter() 非常相似,但不是创建新上下文(变量 this),而是传递 Foo的上下文。

例如,这里是 EventEmitter 的构造函数 source:

function EventEmitter() {
   this._events = new Events();
   this._eventsCount = 0;
}

以上 class 成员(this._eventsthis._eventCount)对于维护事件发射器的私有状态是必需的。
仅应用 inherits(Foo, EventEmitter) 将使用 EventEmitter 的方法增强 Foo 的实例,但是 Foo 的实例将缺少非常基本和关键的初始化过程。

如果 super class 有一个空的构造函数,你可以跳过这一步,因为它没有任何东西可以分配给 this 变量。话虽如此,这是一种不好的做法,因为您无法保证这一点。