JavaScript中的this是一個比較繞的問題,有很是很是多的文章在講這件事,這裏推薦一篇文章,看了這篇文章基本上就能弄明白了。html
這篇文章講了關於this的一個基本原則:瀏覽器
包含this的Function是看成方法調用的,仍是看成函數調用?閉包
若是是obj.func()這一類型的,那麼就是方法調用,若是是func()這一類型的那麼就是函數調用,在方法調用中,this就是obj自己的引用,若是是函數調用,狀況就複雜了,app
好了,背景交待完了,咱們說來看本文的想講的問題: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();
咱們也能夠嘗試在 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();
還...真...見...鬼...了...
貌似在函數調用的場景下,竟然還能使用 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();
如咱們上面所講,這個 Foo.prototype.evt.on 的回調函數中,this指的是 EventEmitter 對象,那如何才能調到 Foo 對象實例呢?此處咱們稍加修改,代碼以下:
在設計時將匿名函數中的 this 強行綁定( bind )爲 Foo.prototype 對象,這樣就能夠在匿名函數中獲取 Foo 對象實例了。