两种基本写法
person.js – 写法1
exports.hello = function(){
console.log(‘world’);
}
person.js – 写法2
var person = {
hello : function(){
console.log(‘world’);
}
}
module.exports = person;
user-person.js
var person = require(‘person’);
person.hello();
一种错误的写法
bar.js
var bar = function(){
console.log(‘it is bar’);
};
exports = bar;
use-bar.js
var bar = require(‘./bar.js’);
bar(); // 这里会报错:TypeError: object is not a function
使用 require 加载模块时返回 module.exports,exports 是 module.exports 的引用(var exports = module.exports),代码“exports=bar” 改变了 exports 的引用,使 exprts 指向了 bar,而不是 module.exports。所以最终返回的 module.exports 并没有被赋值,它只是一个空对象,使用时抛出 TypeError 的错误。
正确的写法如下:
var bar = function(){
console.log(‘it is bar’);
};
module.exports = bar;
集合
models.js
exports.User = require('./user');
exports.Person = require('./person');
user-models.js
var models = require('./models');
User = models.User;
Product = models.Product;
实际工程中,如果这些模型的模块文件都在同一个目录下面,我们可以使用一条语句就可以全部加载进行并赋值给 module.exports 进行暴露。
models.js
module.exports = require('../lib/models')(__filename);
构造方法
person.js
function Person(name) {
this.name = name;
}
Person.prototype.hello = function() {
console.log("Hi, I'm " + this.name);
};
module.exports = Person;
user-person.js
var Person = require('./person');
var person = new Person('Jane');
工场方法
person-factory.js
function Person(name) {
this.name = name;
}
Person.prototype.hello = function() {
console.log("Hi, I'm " + this.name);
};
module.exports = function(name){
return new Person(name);
};
user-person-factory.js
var factory= require('./person-factory');
var person = factory('Jane');
person.hello();
单例的写法
由于 Node 根据模块的绝对路径对模块进行缓存,所以模块直接返回对象时,该对象会被缓存,每次使用这个模块都会返回同一个对象。
function Mongoose() {
//...
}
// 重新暴露构造函数(额外做法,与单例无关)
Mongoose.prototype.Mongoose = Mongoose;
module.exports = exports = new Mongoose();
偏函数
例1
var isType = function(type){
return function(obj){
let str = toString.call(obj);
return str == '[object ' + type + ']';
};
};
exports.isString = isType('String');
exports.isFunction = isType('Function');
例2
var qs = require('./qs');
var parse = require('./parseUrl').parseUrl;
module.exports = function query(options) {
return function query(req, res, next) {
if (!req.query) {
req.query = ~req.url.indexOf('?') ? qs.parse(parse(req).query, options) : {};
}
next();
};
};
var connect = require('connect');
var query = require('connect/lib/middleware/query');
var app = connect();
app.use(query({maxKeys: 100}));
全局对象
should.js
var should = function (obj) {
return new Assertion(util.isWrapperType(obj) ? obj.valueOf() : obj);
};
//...
exports = module.exports = should;
//...
Object.defineProperty(Object.prototype, 'should', {
set: function () {
},
get: function () {
return should(this);
},
configurable: true
});
use-should.js
require('should');
var user = {
name: 'Jane'
};
user.name.should.equal('Jane');
Monkey Patch
Monkey Patch 直译出来是猴子补丁,它的意思是在运行时动态修改某个类或者模块,多用于给第三方代码打补丁,一般用于修改第三方代码的bug,或者是添加一些没有的功能。
我们还是来看下 mongoose 这个模块,默认情况它会将 model 的名称转换为小写和复数的形式,例如如果我们将模块的名称命名为 CreditCardAccountEntry,那么它对应的 collection 的名称就是 creditcardaccountentries。但实际上这个名称非常难以阅读,通常我更喜欢使用credit_card_account_entries,而且我希望这能够作为一种通用模式。
这里我只有给 mongoose.model 打补丁,代码如下:
mongoose-model-patch.js
var Mongoose = require('mongoose').Mongoose;
var _ = require('underscore');
var model = Mongoose.prototype.model;
var modelWithUnderScoreCollectionName = function (name, schema, collection, skipInit) {
collection = collection || _(name).chain().underscore().pluralize().value();
model.call(this, name, schema, collection, skipInit);
};
Mongoose.prototype.model = modelWithUnderScoreCollectionName;
当这个模块第一次被加载的时候,它会加载mongoose,重新定义Mongoose.prototype.model,这里使用了代理模式,最终这个新的model方法也会使用原来的model方式来实现对应的功能。现在所有Mongoose的实例都有这个新的行为。注意,这里没有给exports赋值,所以使用require加载这个模块是时候返回是空对象,这也说exports所表示的默认值。
这里有一点需要注意的,当你要采用这种模式来改变第三方模块的行为的时候,最好是采用这里所用的方式,采用代理模式,尽可能用第三方模块提供的默认行为了完成你的行为,这可以保证在第三方模块更新后还可以继续使用更新后的功能。
参考
https://gywbd.github.io/posts/2014/11/using-exports-nodejs-interface-design-pattern.html