我正在学习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
答案 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
答案 1 :(得分:2)
申请时:
inherits(Foo, EventEmitter);
Foo
的原型设置为EventEmitter.prototype
。因此,Foo
的每个实例都将包含所有EventEmitter
方法(例如on
,emit
等。)
申请时:
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
可以说是Foo
和EventEmitter
的实例。在其他语言中,您可以设置语言和编译器支持的更明确的继承。
.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