EventEmitter事件處理器中的this問題

JavaScript中的this是一個比較繞的問題,有很是很是多的文章在講這件事,這裏推薦一篇文章,看了這篇文章基本上就能弄明白了。html

這篇文章講了關於this的一個基本原則:瀏覽器

包含this的Function是看成方法調用的,仍是看成函數調用?閉包

若是是obj.func()這一類型的,那麼就是方法調用,若是是func()這一類型的那麼就是函數調用,在方法調用中,this就是obj自己的引用,若是是函數調用,狀況就複雜了,app

1. 瀏覽器內

  1.1 嚴格模式下:undefined
  1.2 非嚴格模式下:window

2. Node.js(詳述見這裏)

  2.1 函數內部
    2.1.1 嚴格模式下:undefined
    2.1.2 非嚴格模式下: global
  2.2 全局代碼
    2.2.1 老是modeul.exports對象

好了,背景交待完了,咱們說來看本文的想講的問題:ide

下面是一段簡單的代碼及其執行結果。根據咱們上面背景知識中的理論,在 gateData 方法中, http.get 方法的回調函數將會被用作函數執行,所以在函數裏面取不到Foo的實例對象,所以只能使用 that 來引用Foo構造函數的實例 。函數

'use strict';
var http = require('http');
var util = require('util');
var EventEmitter = require('events');

function Foo() { }

util.inherits(Foo, EventEmitter);

Foo.prototype.print = function (text) {
    console.log(text);
}

Foo.prototype.getData = function () {
    var that = this;
    http.get('http://www.kuaidi100.com/autonumber/autoComNum?text=4254253244', function (res) {
        var output = '';
        res.setEncoding('utf8');

        res.on('data', function (chunk) {
            output += chunk;
        });

        res.on('end', function () {
            that.print('method invoke:' + output);
        });
    });
}

var foo = new Foo();

foo.getData();
View Code

 咱們也能夠嘗試在 http.get 方法的回調函數中訪問 this ,就會出現以下的錯誤。ui

這說明咱們前面的背景知識是沒有問題的,那麼問題來了:this

若是我要寫爲Foo對象的 prototype 添加一個事件處理器(你們可能注意到了,Foo構造函數已經繼承了 EventEmitter ),那麼在事件處理器中如何引用Foo對象實例?spa

根據上面的 getData 方法狀況,極可能以爲這個事件處理器也很像一個函數調用,因此在某個地方要先建立 this 的閉包引用,而後在裏面用 that 引用。然而在哪裏才能建立這個閉包變量呢?這可就尷尬了...(咱先不說把事件處理器定義在構造函數中的狀況)prototype

額...要麼咱先在事件處理器中試試 this 對象,萬一要是見鬼了呢:)

'use strict';
var http = require('http');
var util = require('util');
var EventEmitter = require('events');

function Foo() { }

util.inherits(Foo, EventEmitter);

Foo.prototype.print = function (text) {
    console.log(text);
}

Foo.prototype.getData = function () {
    var that = this;
    http.get('http://www.kuaidi100.com/autonumber/autoComNum?text=4254253244', function (res) {
        var output = '';
        res.setEncoding('utf8');

        res.on('data', function (chunk) {
            output += chunk;
        });

        res.on('end', function () {
            that.print('method invoke:' + output);
            that.emit('evt', 'event handler:' + output);
        });
    });
}

Foo.prototype.on('evt', function (text) {
    this.print(text);
});

var foo = new Foo();

foo.getData();
View Code

還...真...見...鬼...了...

貌似在函數調用的場景下,竟然還能使用 this ,難道理論有漏洞?這隻能到 EventEmitter 中一探究竟了,看看裏面到底發生了什麼

 EventEmitter.addListener 方法

 EventEmitter._addListener 方法

 EventEmitter.emit 方法

 

 EventEmitter.emitNone 方法

咱們知道,若是使用 call 或 apply 方法,則會指定被調用函數的上下文,即 this 對象,難怪在事件處理器中,在回調函數裏面能夠用 this 引用Foo對象實例了

--------------------------------補充--------------------------------

忽然想到一個小問題,若是將要建立的對象,並無繼承自EventEmitter,而是「組合」的EventEmitter,那麼如何在設計時就偵聽方法呢?

'use strict';
var http = require('http');
var EventEmitter = require('events');

function Foo() { }

Foo.prototype.evt = new EventEmitter();

Foo.prototype.print = function (text) {
    console.log(text);
}

Foo.prototype.getData = function () {
    var that = this;
    http.get('http://www.kuaidi100.com/autonumber/autoComNum?text=4254253244', function (res) {
        var output = '';
        res.setEncoding('utf8');

        res.on('data', function (chunk) {
            output += chunk;
        });

        res.on('end', function () {
            that.print('method invoke:' + output);
            that.evt.emit('fire', 'event handler:' + output);
        });
    });
}

Foo.prototype.evt.on('fire', function (text) {
    this.print(text);
});

var foo = new Foo();

foo.getData();
View Code

如咱們上面所講,這個 Foo.prototype.evt.on 的回調函數中,this指的是 EventEmitter 對象,那如何才能調到 Foo 對象實例呢?此處咱們稍加修改,代碼以下:

在設計時將匿名函數中的 this 強行綁定bind )爲 Foo.prototype 對象,這樣就能夠在匿名函數中獲取 Foo 對象實例了。

相關文章
相關標籤/搜索