如何在node.js中的多个文件之间拆分类定义?

时间:2016-06-30 13:30:49

标签: node.js require node-modules

Foo的我的班级定义已经发展到我希望将其拆分为多个文件的程度。例如,我喜欢这样的东西:

// file foo.js
'use strict';
function Foo() { };
Foo.prototype.constructor = Foo;
Foo.prototype.methodA = function() { console.log('methodA'); };
module.exports = Foo;

// file foo-aux.js
'use strict';
Foo.prototype.methodB = function() {
  console.log('methodB');
  this.methodA();
};

// file main.js
'use strict';
const Foo = require('./foo');
var foo = new Foo();
foo.methodB();

module.exportrequire()的正确组合是什么使上述代码有效?

2 个答案:

答案 0 :(得分:8)

更新回答

McMath's detailed answer的完全道具 - 它比下面的原始答案更好地扩展,并提供更好的代码重用的机会。它推动我更新这个答案。但如果你不需要在那里支持全面的mixin路线,这里是一个干净而简单的方法。)

修改technique suggested by Shaun Xu,您可以将主类定义作为参数传递给require,这将在拆分文件中接收。

使用index.js创建一个子目录来保存类定义和子文件是一个好习惯 - 这清楚地表明子文件是Foo的一部分类:

main.js
foo/
  index.js
  foo-a.js
  foo-b.js

以下内容:

// foo/index.js
'use strict';
function Foo() {};
Foo.prototype.constructor = Foo;
require('./foo-a')(Foo);
require('./foo-b')(Foo);
module.exports = Foo;

// foo/foo-a.js
'use strict';
module.exports = function(Foo) { 
  Foo.prototype.methodA = function() {
    console.log('methodA');
  };
  // more methods as desired...
};

// foo/foo-b.js
'use strict';
module.exports = function(Foo) { 
  Foo.prototype.methodB = function() {
    console.log('methodB');
    this.methodA();
  };
  // more methods as desired...
};

并称之为:

// main.js
'use strict';
const Foo = require('./foo/');
var foo = new Foo();
foo.methodB();

原始答案

// file foo.js
'use strict';
function Foo() { };
Foo.prototype.constructor = Foo;
Foo.prototype.methodA = function() { console.log('methodA'); };
require('./foo-aux')(Foo);  // <== add this line
module.exports = Foo;

// file foo-aux.js
'use strict';
module.exports = function(Foo) {  // <== wrap function definitions 
  Foo.prototype.methodB = function() {
    console.log('methodB');
    this.methodA();
  };
};

// file main.js
'use strict';
const Foo = require('./foo');
var foo = new Foo();
foo.methodB();

// test
$ node foo.js
methodB
methodA

答案 1 :(得分:5)

我会考虑两种解决方案,具体取决于我是想为每个文件定义一个方法还是在一个文件中将多个相关方法分组。

每个文件一个方法

从这样的目录结构开始:

foo/
  foo.a.js
  foo.b.js
  index.js
main.js

Foo的其中一种方法可能如下所示:

// foo/foo.a.js

module.exports = function() {
  console.log('Method A');
};

可以以类似的方式定义另一种方法。 Foo本身可以这样定义:

// foo/index.js

function Foo() { }

Foo.prototype.methodA = require('./foo.a');
Foo.prototype.methodB = require('./foo.b');

module.exports = Foo;

现在我们可以像这样使用Foo

// main.js

var Foo = require('./foo');

var foo = new Foo();

foo.methodA(); // 'Method A'
foo.methodB(); // 'Method B'

此解决方案的一个优点是Foo的所有方法都在一个地方声明,即在foo/index.js中,但在其他地方定义。它立即清楚地查看一个文件Foo有哪些方法,而不会实现它们的所有混乱。

每个文件多个方法

在这种情况下,我倾向于使用mixin模式。这是目录结构:

/foo
  bar.js
  baz.js
  index.js
/utils
  extend.js
  mixin.js
main.js

从一个扩展一个对象的函数开始,包括getter / setter,并保持相同的property descriptor

// utils/extend.js

module.exports = function extend(target, source) {
  var names = Object.getOwnPropertyNames(source);
  var len = names.length;

  for (var i = 0; i < len; i++) {
    var name = names[i];
    var descriptor = Object.getOwnPropertyDescriptor(source, name);

    Object.defineProperty(target, name, descriptor);
  }
};

mixin只对两个对象的原型执行此操作:

// utils/mixin.js

var extend = require('./extend');

module.exports = function mixin(target, source) {
  extend(target.prototype, source.prototype);
};

现在我们可以像这样定义Bar基类:

// foo/bar.js

function Bar(a, b) {
  this.a = a;
  this.b = b;
}

Bar.prototype.methodA = function() {
  console.log(this.a);
};

Bar.prototype.methodB = function() {
  console.log(this.b);
};

module.exports = Bar;

Baz可以类似地定义。那么扩展两者的Foo可以这样定义:

// foo/index.js

var Bar = require('./bar');
var Baz = require('./baz');
var mixin = require('../utils/mixin');

function Foo(a, b, c, d) {
  Bar.call(this, a, b);
  Baz.call(this, c, d);
}

mixin(Foo, Bar);
mixin(Foo, Baz);

module.exports = Foo;

我们可以像这样使用它:

// main.js

var Foo = require('./foo');

var foo = new Foo('one', 'two', 'three', 'four');

foo.methodA(); // 'one'
foo.methodB(); // 'two'
foo.methodC(); // 'three'
foo.methodD(); // 'four'

此方法的一个优点是我们可以自己BarBaz或扩展其他类。而且,每个都有自己的构造函数的事实让我们在它们被定义的文件中声明它们的依赖关系,而不是说,必须记住在this.a构造函数中分配Foo属性。