什么是事件发射器呼叫?

时间:2015-06-06 16:17:34

标签: javascript node.js prototypal-inheritance

我正在学习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();

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

代码来源:Basarat Ali Syed的Beginning Node.js

3 个答案:

答案 0 :(得分:6)

代码行:

EventEmitter.call(this);

调用您继承的对象的构造函数,该对象允许EventEmitter代码初始化此对象的一部分,该部分是Javascript中继承过程的一部分。

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

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

以下是chaining constructors主题的一个很好的参考。

从您的一些评论中可以看出,您不了解代码中的继承点是什么。该代码允许您创建一个对象类型Foo,其上有自己的方法,但该对象也是一个eventEmitter,具有EventEmitter的所有功能,可以触发事件,响应事件等。 ..这被称为"继承"您使用自己的自定义对象继承其他对象的功能。为了使继承有效,您的代码会做两件事。使用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

答案 1 :(得分:2)

申请时:

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;
}

上述班级成员(this._events& this._eventCount)是维护事件发射器私密状态所必需的。
仅应用inherits(Foo, EventEmitter)将使用Foo方法增强EventEmitter个实例,但Foo的实例将缺少非常基本且关键的初始化过程。

如果超类具有空构造函数,则可以跳过此步骤,因为它没有任何内容可以分配给this变量。话虽如此,这是一种不好的做法,因为你无法保证这一点。

答案 2 :(得分:-2)

这是ghetto JS继承的方式,因为该语言只支持原型继承。像其他语言一样,没有官方语言支持的类继承,但是我们可以针对不同的上下文运行函数这一事实已成为继承层次结构中的一个非常标准的黑客攻击,其中类的实例也可以被视为其实例基类。换句话说,在var foo = new Foo()中,foo可以说是FooEventEmitter的实例。在其他语言中,您可以设置语言和编译器支持的更明确的继承。

.call可用于所有函数,并允许您执行该函数,但具有不同的上下文。 this类中的Foo引用了Foo的实例,EventEmitter.call(this);正在运行EventEmitter构造函数,但使用的是Foo的实例} this构造函数中的EventEmitter。这样,EventEmitter构造函数通常会通过EventEmitter在纯var emitter = new EventEmitter();的新实例上设置的任何内容现在实际上都会在您的 {的实例上设置{1}}。

现在,这只解决了实现JS伪继承的一半目标。如果Foo原型上有任何内容需要我们的EventEmitter原型,那么仅仅调用Foo实例上的EventEmitter构造函数是不够的。这就是您还必须致电Foo

的原因

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