惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

B
Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Microsoft Security Blog
Microsoft Security Blog
B
Blog RSS Feed
云风的 BLOG
云风的 BLOG
G
Google Developers Blog
Recent Announcements
Recent Announcements
A
About on SuperTechFans
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Google Online Security Blog
Google Online Security Blog
Google DeepMind News
Google DeepMind News
S
Schneier on Security
S
Secure Thoughts
T
The Exploit Database - CXSecurity.com
Martin Fowler
Martin Fowler
P
Proofpoint News Feed
Security Latest
Security Latest
Jina AI
Jina AI
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Recorded Future
Recorded Future
T
Tor Project blog
有赞技术团队
有赞技术团队
H
Hackread – Cybersecurity News, Data Breaches, AI and More
N
News | PayPal Newsroom
博客园 - 三生石上(FineUI控件)
MyScale Blog
MyScale Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Last Week in AI
Last Week in AI
F
Full Disclosure
Hacker News: Ask HN
Hacker News: Ask HN
Forbes - Security
Forbes - Security
D
DataBreaches.Net
人人都是产品经理
人人都是产品经理
NISL@THU
NISL@THU
C
Cisco Blogs
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Google DeepMind News
Google DeepMind News
Project Zero
Project Zero
IT之家
IT之家
T
Threatpost
Cyberwarzone
Cyberwarzone
O
OpenAI News
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
J
Java Code Geeks
P
Proofpoint News Feed
The Last Watchdog
The Last Watchdog
月光博客
月光博客
Latest news
Latest news
MongoDB | Blog
MongoDB | Blog
Apple Machine Learning Research
Apple Machine Learning Research

博客园 - think8848

[原]使用BPI-R4开发板实现5G上网、Wi-Fi AP、文件共享和Docker服务 [原]挂载oVirt虚拟机磁盘 mongocxx-driver编译安装 Mapnik 3.0.20编译安装 [原]Webpack 3 + AngularJS1.* + Bootstrap 4 + Mapbox-gl VSCode下调试mocha测试用例 Mapbox Studio Classic 闪退问题解决方案 Rappid 消除试用版的弹出框 CentOS7.2 1511部署RabbitMQ shp2pgsql向postgresql导入shape数据 node.js的Promise库-bluebird示例 [原]Docker部署SuperMap8.1.1 Docker初步 node.js 调试问题 [原]OpenStreetMap数据瓦片服务性能篇 [原]CentOS7.2部署KVM虚拟机 [原]使用node-mapnik和openstreetmap数据初步搭建瓦片服务 [原]使用node-mapnik生成openstreetmap-carto风格的瓦片 [原]CentOS7.2部署node-mapnik
EventEmitter事件处理器中的this问题
think8848 · 2017-07-09 · via 博客园 - think8848

JavaScript中的this是一个比较绕的问题,有非常非常多的文章在讲这件事,这里推荐一篇文章,看了这篇文章基本上就能弄明白了。

这篇文章讲了关于this的一个基本原则:

包含this的Function是当作方法调用的,还是当作函数调用?

如果是obj.func()这一类型的,那么就是方法调用,如果是func()这一类型的那么就是函数调用,在方法调用中,this就是obj本身的引用,如果是函数调用,情况就复杂了,

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对象

好了,背景交待完了,我们说来看本文的想讲的问题:

下面是一段简单的代码及其执行结果。根据我们上面背景知识中的理论,在 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 ,就会出现如下的错误。

这说明我们前面的背景知识是没有问题的,那么问题来了:

如果我要写为Foo对象的 prototype 添加一个事件处理器(大家可能注意到了,Foo构造函数已经继承了 EventEmitter ),那么在事件处理器中如何引用Foo对象实例?

根据上面的 getData 方法情况,很可能觉得这个事件处理器也很像一个函数调用,所以在某个地方要先创建 this 的闭包引用,然后在里面用 that 引用。然而在哪里才能创建这个闭包变量呢?这可就尴尬了...(咱先不说把事件处理器定义在构造函数中的情况)

额...要么咱先在事件处理器中试试 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 对象实例了。