如何将 class 定义拆分到 node.js 中的多个文件?

How do I split a class definition across multiple files in node.js?

我对 Foo 的 class 定义已经发展到我想将其拆分为多个文件的程度。例如,我想要这样的东西:

// 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() 的正确组合是什么?

更新答案

的完整支持——它比下面的原始答案更好地扩展,并提供了更好的代码重用的机会。它促使我更新这个答案。但是如果你不需要那里支持完全混合路线,这是一种干净简单的方法。)

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

最好创建一个包含 index.js 的子目录来保存 class 定义和子文件——这清楚地表明子文件是 [=16] 的一部分=] class:

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

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

每个文件一个方法

从这样的目录结构开始:

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

从一个对象扩展另一个对象的函数开始,包括 getters/setters 并保持相同的 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 基数 class:

// 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 或扩展其他 classes。此外,每个都有自己的构造函数这一事实让我们可以在定义它们的文件中声明它们的依赖关系,而不是说,必须记住在 [=20] 中分配 this.a 属性 =]构造函数。