Block Rockin’ Codes

back with another one of those block rockin' codes

require('events').EventEmitter.call(this) の意味

[修正]
コメントで指摘されたように、回答4の訳が間違っていたので訂正しました。


Node の ML に以下のような質問が投稿されました。

What is the meaning of require('events').EventEmitter.call(this)

内容としては。


「以下のようなコードがあったんだけど、これってどういう意味?」

var util = require("util");
var events = require("events");

function MyStream() {
  // ここの意味がよくわからん、これは `new MyStream` と同じに見えるんだけど違うの?
  events.EventEmitter.call(this);
}

util.inherits(MyStream, events.EventEmitter);

var steam = new MyStream();


というもの。

これは結構良い質問で、 Node でプログラムをする時の大事なイディオムが詰まってるように感じました。
このスレッドにはいくつかの回答が上がったので、それらを紹介しながら、このイディオムを少し解説してみようと思います。


以下、まず ML でのやり取りの大雑把な訳(ちょいちょい省いてます)を載せて、その後にちょっと解説をします。

ML でのやりとり

投稿順ではなく、質問に対する回答の順になってます。

回答1

これは、 「MyStream のコンストラクタで、EventEmitter のコンストラクタを呼ぶ」 という意味。
util.inherits を一緒に使って、 MyStream は EventEmitter になる。
util.inherits については http://nodejs.org/docs/v0.4.8/api/util.html#util.inherits


回答1-2

それは JS のオブジェクト指向に共通するプラクティスだ。
もし自分のクラス内で親クラスのコンストラクタを呼ぶことにより、
自分のクラスを this として親クラスのプロパティを継承したりできる。

function A () { 
  console.log('a constructor'); 
  this.a = 'a' 
};

function B () { 
  A.call(this); 
  console.log('b constructor'); 
  this.b = 'b' 
};


var b = new B();
// => a constructor
// => b constructor
// b.a => 'a';
// b.b => 'b';
質問2

つまり `EventEmitter.call` は EventEmitter のコンストラクタだってこと?
これ(メソッド)はなにしているの?
MyStream 内で `EventEmitter.call` を呼ぶ目的は何?
その辺詳しく頼む。


回答2

あるオブジェクト上で EventEmitter.call は EventEmitter のインスタンスメソッド/プロパティのセットアップを行う(継承ではないよ)。


これは Java の Super() や C# の base(...) にあたるもの。

でも JS にはないので、自分でやらないといけない。


util.inherits については、これで MyStream が 「ある prototype を継承したオブジェクト」 を継承するようになり instanceof が動くようになる。
(ちなみに JS は多重継承はできない。)

new を実行することによって、もしインスタンスを生成しようとするコンストラクタ内に this があって、そこで EventEmitter.call を呼ぶと、
MyStream オブジェクト上で EventEmitter のコンストラクタが呼ばれたように、 (MyStream の this に対して) 処理が行われる。

回答3(izaacs)

補足すると、

JavasSript における "Class" は "prototype" プロパティを持つ単なる "関数" でしかない。

関数オブジェクトが持つ "call" メソッドは、引数に渡されたオブジェクトの "this-context" で関数を実行する。

fn.call(obj, 1, 2, 3)
// ↑は↓と同じ
obj.fn(1, 2, 3)

つまり、 `ParentClass.call(this, x, y, z)` (もしくはより一般的な ParentClass.apply(this, arguments)))
は、 "親クラスのコンストラクタを、あたかも `new ParentClass(x, y, z)` を実行したかのように呼び出す。
でも、新しくオブジェクトを作るのではなく、あくまで自分のオブジェクトに対して。" という意味。

unction Point2D (x, y) {
  this.x = x;
  this.y = y;
}

Point2D.prototype = {
  distance: function (p) {
    return Math.pow(Math.pow(p.x - this.x, 2) + Math.pow(p.y - this.y, 2), 0.5);
  }
};

function Point3D (x, y, z) {
  // 親クラス(Point2D)のコンストラクタを呼ぶ
  Point2D.call(this, x, y);
  this.z = z;
}

// 親クラスのプロトタイプを継承する
Point3D.prototype = Object.create(Point2D.prototype, {
  constructor: {
    value: Point3D
  }
});

// distance メソッドをオーバーライド
Point3D.prototype.distance = function (p) {
  return Math.pow(Math.pow(p.x - this.x, 2) + Math.pow(p.y - this.y,2) + Math.pow(p.z - this.z, 2), 0.5);
};
質問4

コンストラクタなんか呼ばなくても、 EventEmitter を(utill.inherit で)継承した時点で、
event はちゃんと動くんじゃないの?なんで call を呼ばないといけないの?

回答4(izaacs)(訂正)

今の実装では、実際は何もしない(つまりコンストラクタに機能が無い)。
でももしいずれ、何かの機能がEventEmitterのコンストラクタに実装された場合、
きっとそれを使いたくなるよね。(だから call() しておく方がいい。)
だから、もし必要なかったとしても、これは良い習慣なんだ。

質問5

なるほどわかった!


解説

本当は質問が続くけど、スレッドは質問5のところでストップしています。
とりあえず所々補足できそうなところを解説します。

質問内容

そもそも最初の質問に出てきているソースは、

http://nodejs.jp/nodejs.org_ja/api/util.html#util.inherits

にあるソース。

var util = require("util");
var events = require("events");

function MyStream() {
    events.EventEmitter.call(this);
}

util.inherits(MyStream, events.EventEmitter);

MyStream.prototype.write = function(data) {
    this.emit("data", data);
}

var stream = new MyStream();

console.log(stream instanceof events.EventEmitter); // true
console.log(MyStream.super_ === events.EventEmitter); // true

stream.on("data", function(data) {
    console.log('Received data: "' + data + '"');
})
stream.write("It works!"); // Received data: "It works!"


このソースの中にある EventEmitter の継承について彼は質問している。

require('event').EventEmitter

EventEmitter とは、 Node でのイベント駆動プログラムの根幹をなす重要なオブジェクトで、
自分で作ったオブジェクトがこれを継承することで、イベントを用いた操作、つまり ``on()`` や ``emit()`` が使えるようになる。

その際に、この EventEmitter を継承する際によく行われるのが以下のイディオム。

function MyStream() {
  events.EventEmitter.call(this);
}
util.inherits(MyStream, events.EventEmitter);

これで自作の MyStream から生成したインスタンスは、 EventEmitter を継承する。

call(this)

ここが、質問の中心になっていた部分。
EventEmitter.call(this) で自分自身を this として EventEmitter のコンストラクタを実行する。このへんは上の回答にある通り。 JavaScript の共通イディオム。

util.inherits()

説明としては、 http://nodejs.jp/nodejs.org_ja/api/util.html#util.inherits にある通りで、 util モジュールにある通り便利メソッドなんだけど、実装を見ると実は結構短い。

こんだけ。コメントは長いのではしょった。ソースはこちら

/**
 * Inherit the prototype methods from one constructor into another.
 *
 * (略)
 *
 * @param {function} ctor Constructor function which needs to inherit the
 *     prototype.
 * @param {function} superCtor Constructor function to inherit prototype from.
 */
exports.inherits = function(ctor, superCtor) {
  ctor.super_ = superCtor;
  ctor.prototype = Object.create(superCtor.prototype, {
    constructor: {
      value: ctor,
      enumerable: false,
      writable: true,
      configurable: true
    }
  });
};

つまり先ほどのこれは、

// events.EventEmitter は EventEmitter に書き換えると。
util.inherits(MyStream, EventEmitter);

MyStream.super_ というプロパティに(あとで便利なように) EventEmitter を加え、
MyStream.prototype に EventEmitter をプロトタイプとするオブジェクトを加えている。
つまり EventEmitter を継承したオブジェクトを MyStream のプロトタイプにしている。
(なかなか言葉にしづらい。中間オブジェクトがあるイメージ。)

次に Object.create() と constructor プロパティも補足しておく。

Object.create()

Object.create() は ECMA5 のメソッドで第一引数に受け取ったオブジェクトをプロトタイプに持つオブジェクトを生成します。
シミュレートすると以下のような感じ。

Object.create = function(p) {
  function f();
  f.prototype = p;
  return new f();
}

しかし、 Object.create() は null を受け取る事もできるため、「なにも継承しない」オブジェクトも作る事が可能です。
これは上のようにシミュレートすることはできません。

constructor プロパティ
ctor.prototype = Object.create(superCtor.prototype, {
  constructor: {
    value: ctor,
    enumerable: false,
    writable: true,
    configurable: true
  }
});

さっきのこれは、 superCtor.prototype をプロトタイプにしながら、さらに一つ constructor というプロパティをセットしている。

constructor プロパティを指定しないと、 Object.create() したオブジェクトの constructor プロパティは、第一引数に渡したコンストラクタになる。
それを防ぐために、明示的に constructor プロパティをセットしている。ここでは ECMA5 のプロパティディスクリプタによって、プロパティの細かい属性も指定されていて、

value
プロパティの値=ctor
enumerable
列挙(for ~ in でイテレート)可能か=false
writable
値を更新可能か=true
configurable
Property Descripter を変更可能か=true

とそれぞれ指定されている。
プロパティディスクリプタについて、詳しくはこの辺が参考になる。 ECMAScript 5の"Object" - JavaScriptで遊ぶよ - g:javascript


つまり、このオブジェクトは ctor をコンストラクタとして生成されたことにしている。
という感じ。

まとめ

最後に、そもそもの質問に立ち返ると

var util = require("util");
var events = require("events");

function MyStream() {
  // ここの意味がよくわからん、これは `new MyStream` と同じに見えるんだけど違うの?
  events.EventEmitter.call(this);
}

util.inherits(MyStream, events.EventEmitter);

var steam = new MyStream();


これで、 MyStream() から生成したオブジェクトは MyStream を this として EventEmitter のコンストラクタを実行して各初期化を行い、events.EventEmitter がプロトタイプチェーンにあるから、メソッドやプロパティが参照できる。
つまり MyStream() を EventEmitter を継承させたということでした。


これは自分でイベントを必要とするオブジェクトを作る際に使うイディオムなので、覚えておくと参考になると思います。