Features always grows much faster than we expect. When I am build Worktile Pro I created a JavaScript file contains all business logic in class for the task module. But after several months development it became over 7000 lines of code, which is horrible. Last week I decided to split it into multiple files.
It may not be a big problem to split one JavaScript file into multiple, especially in Node.js environment. We can put functions and variables into many files as we wanted and "require" them in the "main" file. But if we want to split a class definition into multiple files that might not work. In JavaScript a class is a function in essential, and it can be defined only in one file. For example, in the code below I defined a class named "MissionService" with some method in file "mission.js".
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
(function () {
'use strict';
var MissionService = function () {
};
MissionService.prototype.createTask = function (taskName) {
console.log('Task: "' + taskName + '" was created.');
};
MissionService.prototype.loadTask = function (taskId) {
console.log('Task (' + taskId + ') was loaded.');
};
MissionService.prototype.updateTask = function (taskId, taskName) {
console.log('Task (' + taskId + ') was changed to "' + taskName + '".');
};
MissionService.prototype.removeTask = function (taskId) {
console.log('Task (' + taskId + ') was removed.');
};
MissionService.prototype.restoreTask = function (taskId) {
console.log('Task (' + taskId + ') was restoreTask.');
};
exports = module.exports = MissionService;
})();
|
First step is to move the class definition into an "index" file, which will "require" all following files later. As you can see this "index.js" file only contains the class definition and exports it.
1
2
3
4
5
6
7
8
9
|
(function () {
'use strict';
var MissionService = function () {
};
exports = module.exports = MissionService;
})();
|
Now we can create a "partial" class definition file based on the "index" I created. Just exports a function which allow the class can be passed so that I can define its methods through "PartialClass.prototype".
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
// mission.create.js
(function () {
'use strict';
exports = module.exports = function (MissionService) {
MissionService.prototype.createTask = function (taskName) {
console.log('Task: "' + taskName + '" was created.');
};
};
})();
// mission.update.js
(function () {
'use strict';
exports = module.exports = function (MissionService) {
MissionService.prototype.updateTask = function (taskId, taskName) {
console.log('Task (' + taskId + ') was changed to "' + taskName + '".');
};
MissionService.prototype.removeTask = function (taskId) {
console.log('Task (' + taskId + ') was removed.');
};
MissionService.prototype.restoreTask = function (taskId) {
console.log('Task (' + taskId + ') was restoreTask.');
};
};
})();
// mission.find.js
(function () {
'use strict';
exports = module.exports = function (MissionService) {
MissionService.prototype.loadTask = function (taskId) {
console.log('Task (' + taskId + ') was loaded.');
};
};
})();
|
Now back to the "index" file, what we need to do is to "require" this partial class file, put the class we defined into the parameter so that it will attach methods.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
(function () {
'use strict';
var MissionService = function () {
};
require('./mission.create.js')(MissionService);
require('./mission.update.js')(MissionService);
require('./mission.find.js')(MissionService);
exports = module.exports = MissionService;
})();
|
Finally when we want to use this class, just "require" the "index" file and "new" an instance as below.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
(function () {
'use strict';
var MissionService = require('./index.js');
var mission = new MissionService();
mission.createTask('Shaun\'s task.');
mission.loadTask(1);
mission.updateTask(1, 'Shaun\'s new task.');
mission.removeTask(1);
mission.updateTask(1);
})();
|

If we have some internal helper functions or variants we can put them into some "shared" files.
1
2
3
4
5
6
7
8
9
10
11
|
(function () {
'use strict';
exports = module.exports = function (MissionService) {
MissionService.prototype._log = function (message) {
console.log(message);
};
};
})();
|
Just ensure we "require" them before we "require" PartialClass files that are using them.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
// index.js
(function () {
'use strict';
var MissionService = function () {
};
require('./mission.shared.js')(MissionService);
require('./mission.create.js')(MissionService);
require('./mission.update.js')(MissionService);
require('./mission.find.js')(MissionService);
exports = module.exports = MissionService;
})();
// mission.create.js
(function () {
'use strict';
exports = module.exports = function (MissionService) {
MissionService.prototype.createTask = function (taskName) {
this._log('Task: "' + taskName + '" was created.');
};
};
})();
// mission.update.js
(function () {
'use strict';
exports = module.exports = function (MissionService) {
MissionService.prototype.updateTask = function (taskId, taskName) {
this._log('Task (' + taskId + ') was changed to "' + taskName + '".');
};
MissionService.prototype.removeTask = function (taskId) {
this._log('Task (' + taskId + ') was removed.');
};
MissionService.prototype.restoreTask = function (taskId) {
this._log('Task (' + taskId + ') was restoreTask.');
};
};
})();
// mission.find.js
(function () {
'use strict';
exports = module.exports = function (MissionService) {
MissionService.prototype.loadTask = function (taskId) {
this._log('Task (' + taskId + ') was loaded.');
};
};
})();
|
At the end, we can put all files into a folder and rename the "index" as "index.js". Now we could require our class by the folder name, which is more friendly.

Hope this helps,
Shaun
All documents and related graphics, codes are provided "AS IS" without warranty of any kind.
Copyright © Shaun Xu. This work is licensed under the Creative Commons License.