2.3 查看 express.js 入口文件
先看导出了哪些
2.3.1 createApplication
设置默认导出了一个createApplication
方法,这个就是我们整个程序的入口。
exports = module.exports = createApplication;
function createApplication() {
var app = function(req, res, next) {
app.handle(req, res, next);
};
mixin(app, EventEmitter.prototype, false);
mixin(app, proto, false);
// expose the prototype that will get set on requests
app.request = Object.create(req, {
app: { configurable: true, enumerable: true, writable: true, value: app }
})
// expose the prototype that will get set on responses
app.response = Object.create(res, {
app: { configurable: true, enumerable: true, writable: true, value: app }
})
app.init();
return app;
}
首先我们看这段代码
var app = function(req, res, next) {
app.handle(req, res, next);
};
诶,这就有点奇怪了,app 是一个函数,为什么又可以调用 app.handle 方法呢?哪来的,不会报错吗?
第一个问题:从哪来的 app.handle ?
看到 mixin 没,虽然我不知道这个 mixin 从哪来的(当然,我也不想关心 mixin 具体用法,至少此刻来说是的),根据我以往的经验来说 mixin 就是混合合并的意思(懂什么作用就行),它这里的意思就是合并 app 与 proto、EventEmitter.prototype。现在 app 就具有他俩的方法,而这个 handle 方法就是在 proto 上面。
第二个问题:不会报错吗?
var app = function(req, res, next) {
app.handle(req, res, next);
};
app() // 报错
这样做确实是会报错,因为我们还没有 mixin 好,通常当我们调用的时候,mixin 已经混合完成,所以就不会报错。
当我们在实际项目中用起来就像这样,这个 app 与上面的 app 相同。
var createApplication = require('express');
var app = createApplication()
接下来看 app.request 与 app.response
// expose the prototype that will get set on requests
app.request = Object.create(req, {
app: { configurable: true, enumerable: true, writable: true, value: app }
})
// expose the prototype that will get set on responses
app.response = Object.create(res, {
app: { configurable: true, enumerable: true, writable: true, value: app }
})
Object.create() 指定原型对象和属性来创建一个新的对象。 更多细节点击这里查看
也就是说上面的这一行实现了以下功能
- app.request.proto = req
- app.request.app = app。
这个是属于原型链继承,众所周知,当一个 js 对象找不到属性的时候,它会往它的__proto__
属性上面去寻找该属性,一直找,直到没有__proto__
为止。
你可能会好奇 mixin 与 create 有什么区别呢?
我们来看看 mixin 的 merge-descriptors 的源码。
function merge(dest, src, redefine) {
if (redefine === undefined) {
redefine = true
}
Object.getOwnPropertyNames(src).forEach(function forEachOwnPropertyName(name) {
if (!redefine && hasOwnProperty.call(dest, name)) {
// dest 已经有 name 属性了
// redefine = true 覆盖掉 dest 的 name 属性
// redefine = false 保持原来 dest 的 name 属性
return
}
// Copy descriptor
var descriptor = Object.getOwnPropertyDescriptor(src, name)
Object.defineProperty(dest, name, descriptor)
})
return dest
}
Object.prototype.hasOwnProperty() -- 返回一个布尔值 ,表示某个对象是否含有指定的属性,而且此属性非原型链继承的。 更多细节请点击
Object.getOwnPropertyDescriptor() -- 方法返回指定对象上一个自有属性对应的属性描述符。更多细节请点击
Object.defineProperty() -- 给对象添加一个属性并指定该属性的配置, 更多细节点击这里查看。
所以说,mixin 是在原来的对象上面 copy 属性进行继承,而 create 是以原型链的方式继承。so 我们又 get 到继承的2种方式。
之后再调用了 app.init() , 这是我们进一步阅读的入口。
2.3.2 其他细枝末节
/**
* Expose the prototypes.
*/
exports.application = proto;
exports.request = req;
exports.response = res;
它这里给出的注释是导出原型,紧接着我们来查看 proto 到底是什么?
移动到最上面, 发现这都是从其他文件里面导入的。现在我们暂时不看其他文件,先把入口文件给看完。
var proto = require('./application');
var Route = require('./router/route');
var Router = require('./router');
var req = require('./request');
var res = require('./response');
以及以下这些导出。
exports.Route = Route;
exports.Router = Router;
/**
* Expose middleware
*/
exports.query = require('./middleware/query');
exports.static = require('serve-static');
这里的 Route 与 Router 、query 都是从其他文件导入的,而 static 是从 node_modules 依赖里面导入的。
[
'json',
'urlencoded',
'bodyParser',
'compress',
'cookieSession',
'session',
'logger',
'cookieParser',
'favicon',
'responseTime',
'errorHandler',
'timeout',
'methodOverride',
'vhost',
'csrf',
'directory',
'limit',
'multipart',
'staticCache',
].forEach(function (name) {
Object.defineProperty(exports, name, {
get: function () {
throw new Error('Most middleware (like ' + name + ') is no longer bundled with Express and must be installed separately. Please see https://github.com/senchalabs/connect#middleware.');
},
configurable: true
});
});
这里有一个数组,通过 forEach 遍历它,为 exports 定义这些 name 属性,设置它的 getter 方法,当调用这个name 属性的时候抛出一个错误,告诉开发者,现在 express 里面已经不包含这些模块了,你需要自己去安装。