express源码阅读

目录

express目录结构

首先我们来分析express目录的结构,假定你已经使用npm把express安装好了。
使用命令查询nodejs包的默认安装目录在哪:

$ npm root
  /home/user/node_modules`
$ ls /home/user/node_modules/
  express  ejs

一般如果没有使用root权限,npm安装的包都在所属用户家目录下名为 node_modules 的文件夹中。
查看express包目录的结构如下:


├── express
│   ├── index.js
│   ├── Readme.js
│   ├── lib
│   │   ├── express.js
│   │   ├── .....
│   │   └── .....
│   ├── node_modules
│   │   ├── anothermodule
│   │   ├── .....
│   │   └── .....

我们发现还有一个也叫 node_modules 的包,这个包中存储了express依赖的其他库,lib文件夹存放了express的源码。express的主文件为 index.js ,主要是导出lib文件夹中express.js文件中的接口,其内容如下:

module.exports = require('./lib/express');
require语法的介绍

require真正导入的是module.exports对象,而非exports,exports是对module.exports的引用,即
exports.name = "my name"; 因为两者引用的是同一个对象,故而此时 module.exports.name == exports.name;

module.exports初始化是一个 {} 空对象,所以如果我们在文件中对exports重新赋值,因为exports对module.exports的指向已经改变,exports已经指向了一个新对象,而require真正导出的是module.exports对象。

如果你想给module.exports赋一个新的对象,旨在让var express = require('express.js')时,让express直接引用那个对象,而不是类似main.express; main.run; main.go;的形式,你可以这么做:

//  /lib/express.js
exports = module.exports = createApplication;
exports.application = proto;
exports.request = req;
exports.response = res;

//  index.js
module.exports = require('./lib/express');

// yourapp.js
var express = require("express");
var app = express();

因为上面将module.exports对象重新赋值了,但exports引用的还是老对象。而你可能又想传递更多的对象,那么你可以这样将exports重新指向为新对象。而你查看源码可以发现,createAppication不是一个函数吗?给函数赋值属性?别忘了,函数也是一个对象,继承自Function类型。而且解释器执行的时候一讲函数提升,函数便会自动拥有一个 Function.prototype 属性。因此下面的语法完全正确:

function create(){
  return "str...";
}

create.age = 22;
create.country = "china";

create()
create.age

注意:函数有一些属性的名称是保留字,不应该乱使用,比如function.name是函数的名称,其特性被设置为不可枚举、只读,还有最熟悉的function.prototype等等。

createApplication函数

通过上面的介绍我们知道 var express = require("express") 实际上express引用的就是 express.js中的函数createApplication,这个文件又额外把一些对象当做函数的属性赋值给了函数。
先来看看这个函数:

var EventEmitter = require('events').EventEmitter;
var mixin = require('merge-descriptors');
var proto = require('./application');
var req = require('./request');
var res = require('./response');

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应该是一个实例或是一个对象,用于get, route, post等方法。但createAppication函数返回了一个app对象,这个对象在函数里又指向了另一个匿名函数。先不去管那个匿名函数,app.get app.post 等方法从哪里来?答案就在 mixin 函数运行后!
mixin变量引用的是另外一个库 merge-descriptors,这个库在express文件夹中的node_modules文件夹可找到,代码很简单都在其index.js文件中:

// merge-descriptors/index.js
module.exports = merge

var hasOwnProperty = Object.prototype.hasOwnProperty

function merge(dest, src, redefine) {
  if (!dest) {
    throw new TypeError('argument dest is required')
  }

  if (!src) {
    throw new TypeError('argument src is required')
  }

  if (redefine === undefined) {
    // Default to true
    redefine = true
  }

  Object.getOwnPropertyNames(src).forEach(function forEachOwnPropertyName(name) {
    if (!redefine && hasOwnProperty.call(dest, name)) {
      // Skip desriptor
      return
    }

    // Copy descriptor
    var descriptor = Object.getOwnPropertyDescriptor(src, name)
    Object.defineProperty(dest, name, descriptor)
  })

  return dest
}

因此mixin函数可传入三个参数 dest, src, redefine,目标函数,源函数,是否需要重新定义。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值