【问题标题】:What is Event Emitter Call?什么是事件发射器调用?
【发布时间】:2015-06-06 16:17:33
【问题描述】:

我正在学习 Node Js。我在书中遇到了一段 sn-p 代码,内容如下:

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

【问题讨论】:

  • “为什么类 Foo 继承自 EventEmitter?” 因为创建Foo 的人希望它的每个实例都可以触发事件并接受事件订阅。如果它不继承自EventEmitter,则无法调用this.emitfoo.on
  • 我想我宁愿你没有删除你的帖子,我去回答它的麻烦。
  • @Dave Newton 我有答案,这是一个简单的错误......对不起

标签: javascript node.js prototypal-inheritance


【解决方案1】:

代码行:

EventEmitter.call(this);

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

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

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

这里有一个很好的参考chaining constructors这个话题。


从您的某些 cmets 看来,您不了解代码中继承的意义。该代码允许您创建一个对象类型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

【讨论】:

  • 那么它会创建一个新的 EventEmitter 实例吗?那么这是否意味着 Foo 是 EventEmitter 的另一种形式?
  • @ExtremelySeriousChicken - 当您执行new Foo() 时,新对象已经创建。 EventEmitter.call(this); 让 EventEmitter 构造函数初始化该对象的一部分。请记住,这是继承,因此多个对象定义使用同一个对象,并且每个对象都必须初始化它们的一部分。
  • @ExtremelySeriousChicken:阅读call的文档:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
  • @ExtremelySeriousChicken - 看来您甚至不了解您正在使用的继承试图做什么,所以我在答案的末尾添加了一段来描述这一点。跨度>
  • @ExtremelySeriousChicken:你可能想读这个:en.wikipedia.org/wiki/…
【解决方案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) 将使用EventEmitter 的方法增强Foo 的实例,但是Foo 的实例将缺少一个非常基本和关键的初始化过程。

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

【讨论】:

    【解决方案3】:

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

    .call 可用于所有函数,并允许您执行函数,但使用不同的上下文。 this 在您的 Foo 类中指的是正在创建的 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 约定,可以轻松访问基本构造函数(您继承自的类的构造函数)。 https://github.com/joyent/node/blob/master/lib/util.js#L634-L644

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多