Express app.listen 函数了解
最近一直在学习如何用原生的 Node.js 来做一个网站。在写的同时也在学习 Express 源码。
一直觉得 Express 开启服务器的方法挺有趣的,就看了一下。
在 Express 运行的时候会默认运行根目录下的 index.js,里面的源码也很简单:
module.exports = require('./lib/express');
看到其实运行了 lib/express
模块,追踪过去,看到了:
exports = module.exports = createApplication;
导出了 createApplication
这个方法:
function createApplication() {
var app = function(req, res, next) {
app.handle(req, res, next);
};
mixin(app, EventEmitter.prototype, false);
mixin(app, proto, false);
app.request = { __proto__: req, app: app };
app.response = { __proto__: res, app: app };
app.init();
return app;
}
首先定义了一个方法 app
,方法有三个参数 req
,res
,next
。然后在这个函数里面又执行了 app.handle
这个函数,这个函数后面说说,涉及到了路由。同时还涉及到了创建服务器函数,下面继续。
再往下看看,会看到 mixin
这个函数,在最上面引入模块的时候,定义了这个函数名:
var EventEmitter = require('events').EventEmitter;
var mixin = require('merge-descriptors');
var proto = require('./application');
var Route = require('./router/route');
var Router = require('./router');
var req = require('./request');
var res = require('./response');
mixin
这个函数是引用了 merge-descriptors
模块,嗯,到 node_module
里面找找,或者到 github
里面找找也可以。
看源码,如果对原生JS的函数不是很熟悉的话,根本看不懂...不过我们可以到 MDN
里面查查,地址懒的贴。我也懒得去介绍里面的各种函数,这个模块的作用和 jQuery
里面的 $.extent
其实是一模一样的,将第二个参数的属性和属性值合并到第一个参数中,第三个参数如果是 false
则如果两个参数里面有属性一样,不允许覆盖,如果是 true
,则覆盖第一个参数属性。
由此可见:
mixin(app, EventEmitter.prototype, false);
mixin(app, proto, false);
这两句话就很好懂了,就是将 EventEmitter.prototype
和 proto
的属性克隆给 app
。
这里看到 proto
肯定也很郁闷...这玩意从哪里来的...还是往上面看:
var proto = require('./application');
额...又是一个模块,继续追踪,第一句话:
var app = exports = module.exports = {};
唉哟,又看到了 app
,这 express
命名也是有趣,都是 app
。我再看的时候也很郁闷,完全会看混淆。继续往下看,原来这个模块的作用就是给 app
添加了各种属性和函数,各种核心函数...各种看不懂的函数...
这里我们找到我们希望看到的一个 listen
这个函数:
app.listen = function listen() {
var server = http.createServer(this);
return server.listen.apply(server, arguments);
};
好烦哟...又碰到看不懂的了,尼玛 this
是什么鬼!!!!我们从 Node.js 的 API 手册可以看到啊,明明官方都说了:
The requestListener is a function which is automatically added to the 'request' event.
this
这个参数的位置明明应该是一个函数啊,但是在 application
里面我们看到的 app
很显然就是一个 {}
对象啊,尼玛,怎么可能是一个 Function
?
好吧,没办法,我们 console.log(this)
试试,看看返回的是什么:
{ [Function]
domain: undefined,
_events: { mount: [Function: onmount] },
_maxListeners: undefined,
setMaxListeners: [Function: setMaxListeners],
getMaxListeners: [Function: getMaxListeners],
emit: [Function: emit],
addListener: [Function: addListener],
on: [Function: addListener],
once: [Function: once],
removeListener: [Function: removeListener],
removeAllListeners: [Function: removeAllListeners],
listeners: [Function: listeners],
listenerCount: [Function: listenerCount],
init: [Function: init],
defaultConfiguration: [Function: defaultConfiguration],
lazyrouter: [Function: lazyrouter],
handle: [Function: handle],
use: [Function: use],
route: [Function: route],
...
第一句居然是 Function
,是什么鬼?什么时候变成了 Function
,仔细看一下,我们发现里面好像有 EventEmitter.prototype
的属性:
EventEmitter {
domain: undefined,
_events: undefined,
_maxListeners: undefined,
setMaxListeners: [Function: setMaxListeners],
getMaxListeners: [Function: getMaxListeners],
emit: [Function: emit],
addListener: [Function: addListener],
on: [Function: addListener],
once: [Function: once],
removeListener: [Function: removeListener],
removeAllListeners: [Function: removeAllListeners],
listeners: [Function: listeners],
listenerCount: [Function: listenerCount] }
哎哟,不错哟,貌似发现了新大陆...明明在 application
这个模块没有添加这个属性啊,从哪里加的?这个时候我们应该顺其自然的想到了:
mixin(app, EventEmitter.prototype, false);
这句话,好像很突然的想到似的...是否在想明明在 express
这个模块加的,为啥在 application
里面会添加进去?OK,到了我们最关键的时候了,我们应该把 Express
的执行顺序理清楚了:
- 在 CMD 或者 终端 执行:node app
- 在我们自己写的
app.js
文件中执行express()
,这里就开始运行express
模块了。 - 进去
express
模块,运行index.js
- 从
index.js
进去lib/express.js
- 在
express.js
中执行createApplication
- 在
createApplication
中首先定义了一个app
的函数,其实刚才我们console.log(this)
的时候,this
返回的就是这个app
函数 - 然后通过
mixin(app, EventEmitter.prototype, false);mixin(app, proto, false);
将EventEmitter.prototype
、proto
里面的属性全部都加到app
这个函数里面 - 执行
app.request
、app.response
这两个就是将重新定义的request
、response
加到app
里面,这两个以后再说 - 继续执行
app.init();
,这句话就简单了啊,init
这个函数在application.js
模块中,主要作用就是给app
加一些初始化设置 - 最后返回
app
- 在我们自己写的
app.js
文件中获取到返回的app
,准备创建服务器app.listen(3000)
- 开始执行
app.listen
这个函数,在app
中找到listen
,这个函数在express.js
中已经从proto
克隆到了app
上了,所以,我们直接到proto
中找listen
,也就是application.js
中去找,找到之后就回到了我们最开始的问题:var server = http.createServer(this);
,this
是什么????看到这里,其实我们已经知道了,this
其实就是var app = function(req,res,next){}
,OK到这里就结束了...
这么晚写这文章,感觉写了一大坨废话...最后的执行顺序才是我最想说的
Express app.listen 函数了解的更多相关文章
- Best practices for Express app structure
Node和Express没有一个相对严格的文件或者是文件夹结构,因此你可以按照自己的想法来构建你的web项目,特别是对一些小的项目来说,他很容易学习. 然而当你的项目变得越来越大,所面临的情况越来越复 ...
- socket编程listen函数限制连接数的解决方案
函数原型: int listen(int sockfd, int backlog); 当服务器编程时,经常需要限制客户端的连接个数,下面为问题分析以及解决办法: 下面只讨论TCP UDP不做讨论(很 ...
- listen函数
listen函数仅仅由TCP服务器调用,它做2件事: 1)当socket函数创建一个套接字时,它被假设为一个主动套接字,也就是说,它是一个将调用connect发起连接的客户套接字 listen函数把一 ...
- tcp/ip协议listen函数中backlog參数的含义
listen函数的定义例如以下所看到的: #include <sys/socket.h> int accept(int sockfd, struct sockaddr * restrict ...
- listen()函数中backlog参数分析
实例分析1 将服务器端的listen函数backlog设置为2,用20个客户端与服务器建立连接,查看连接的建立情况. 服务器代码: #include <stdio.h> #include& ...
- Python中网络编程对 listen 函数的理解
listen函数的第一个参数时SOCKET类型的,该函数的作用是在这个SOCKET句柄上建立监听,至于有没有客户端连接进来,就需要accept函数去进行检查了,accept函数的第一个参数也是SOCK ...
- 网络编程socket之listen函数
摘要:listen函数使用主动连接套接口变为被连接套接口,使得一个进程可以接受其它进程的请求,从而成为一个服务器进程.在TCP服务器编程中listen函数把进程变为一个服务器,并指定相应的套接字变为被 ...
- listen() 函数
声明:本文来自网络博文的合并,文后有链接. 一.listen函数仅由TCP服务器调用 它做两件事: 1.当socket函数创建一个套接字时,它被假设为一个主动套接字,也就是说,它是一个将调用conne ...
- 网络编程:listen函数
listen函数仅由TCP服务器调用,它做两件事: 当socket函数创建一个套接字时,它被假设为一个主动套接字,也就是说,它是一个将调用connect发起连接的客户套接字.listen函数把一个未连 ...
随机推荐
- Silverlight提示“Load 操作失败。远程服务器返回了错误: NotFound”
调试时出现“Load 操作失败.远程服务器返回了错误: NotFound”: 一定要注意此错误之前的错误是什么?基本就是用户Cookie的问题,用户没有登录. 有时需要设置成Any CPU 有时重新编 ...
- JDK动态代理、CGLib动态代理
JDK动态代理源码 一.public static Object newProxyInstance ——> 调用下面这个方法二.Class<?> cl = getProxyClass ...
- [经典bug]弹框关闭按钮点击后程序闪退
问题背景: 业务上遇到一个很诡异的问题:弹框界面上有一个关闭按钮,切换后台再返回后,点击关闭按钮,部分机型上会直接崩溃.点击手机返回键关闭界面则正常. 问题原因: 点击关闭按钮的操作属于UI线程,直接 ...
- LeetCode 题解之Rotate List
1.题目描述 2.题目分析 首先数出链表中的元素个数count,然后对k = k % count 求余数.接着将链表形成一个环,从最后一个元素开始,运动 count - k 步,此时节点的下一个节点 ...
- 【SPL标准库专题(4)】 Datastructures:SplDoublyLinkedList
简述 双链表是一种重要的线性存储结构,对于双链表中的每个节点,不仅仅存储自己的信息,还要保存前驱和后继节点的地址. 类摘要 SplDoublyLinkedList implements Iterato ...
- SqlServer数据库设计一个字段的值是由其他字段运算结果所得
最近在做项目时,发现数据库的一些字段不能执行sql语句进行修改,仔细观察才发现,它是由其他字段运算结果所得.这样就不需程序员通过代码执行运算结果更新数据库,感觉很实用,而网上教材好像还挺少的,所以把教 ...
- 如何加密 Windows VM 上的虚拟磁盘
为了增强虚拟机 (VM) 的安全性以及符合性,可以加密 Azure 中的虚拟磁盘. 磁盘是使用 Azure 密钥保管库中受保护的加密密钥加密的. 可以控制这些加密密钥,以及审核对它们的使用. 本文详细 ...
- pong game using ncurses
bounce2d2.c /* * bounce2d 1.0 * bounce a character (default is 'o') around the screen * defined by s ...
- python基础知识回顾之列表
在python 中,主要的常用数据类型有列表,元组,字典,集合,字符串.对于这些基础知识,应该要能够足够熟练掌握. 如何创建列表: # 创建一个空列表:定义一个变量,然后在等号右边放一个中括号,就创建 ...
- centos 安装elk监控
下面就是要安装一些收集日志 或者分配日志的工具,我选择的是 Filebeat 来收集日志,然后放到kafka中 让kafka这个消息队列来分配生产者消费者 然后通过Logstash 或者一个国产大神 ...