My class definition for Foo
that has grown to the point that I'd like to split it across multiple files. For example, I'd like something like:
// 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();
What's the right combination of module.export
and require()
to make the above code work?
(Full props to McMath's detailed answer -- it scales better than the original answer below and offers the opportunity for better code reuse. And it nudged me into updating this answer. But if you don't need the full-on mixin route espoused there, here's a clean and simple approach.)
Modifying a technique suggested by Shaun Xu, you can pass the primary class definition as an argument to require
which will be received in the split out file(s).
It's good practice to create a subdirectory with index.js
to hold the class definition and the sub-files -- this makes it clear that the sub-files are part of the Foo
class:
main.js
foo/
index.js
foo-a.js
foo-b.js
with the following:
// 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...
};
and to call it:
// 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
There are two solutions that I would consider, depending on whether I wanted to define one method per file or to group multiple related methods in a single file.
Start with a directory structure like this:
foo/
foo.a.js
foo.b.js
index.js
main.js
One of the methods for Foo
might look like this:
// foo/foo.a.js
module.exports = function() {
console.log('Method A');
};
The other method can be defined in a similar way. Foo
itself can be defined like this:
// foo/index.js
function Foo() { }
Foo.prototype.methodA = require('./foo.a');
Foo.prototype.methodB = require('./foo.b');
module.exports = Foo;
Now we can use Foo
like this:
// main.js
var Foo = require('./foo');
var foo = new Foo();
foo.methodA(); // 'Method A'
foo.methodB(); // 'Method B'
One advantage of this solution over your own is that all the methods for Foo
are declared in one place, i.e., in foo/index.js
, but are defined elsewhere. It's immediately clear looking at one file what methods Foo
has, without all the clutter of their implementations.
In this case, I would be inclined to use the mixin pattern. Here is the directory structure:
/foo
bar.js
baz.js
index.js
/utils
extend.js
mixin.js
main.js
Start with a function that extends one object with another, including getters/setters and maintaining the same 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);
}
};
A mixin just does this to the prototypes of two objects:
// utils/mixin.js
var extend = require('./extend');
module.exports = function mixin(target, source) {
extend(target.prototype, source.prototype);
};
Now we can define the Bar
base class like this:
// 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
can be defined similarly. Then Foo
, which extends both, can be defined like this:
// 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;
And we can use it like this:
// 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'
One advantage of this method is that we could potentially Bar
or Baz
by themselves or to extend other classes. Moreover, the fact that each has its own constructor lets us declare their dependencies in the files in which they are defined, rather than, say, having to remember to assign a this.a
property in the Foo
constructor.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With