68
votes

I have a number of JavaScript "classes" each implemented in its own JavaScript file. For development those files are loaded individually, and for production they are concatenated, but in both cases I have to manually define a loading order, making sure that B comes after A if B uses A. I am planning to use RequireJS as an implementation of CommonJS Modules/AsynchronousDefinition to solve this problem for me automatically.

Is there a better way to do this than to define modules that each export one class? If not, how to you name what the module exports? A module "employee" exporting a class "Employee", as in the example below, doesn't feel DRY enough to me.

define("employee", ["exports"], function(exports) {
    exports.Employee = function(first, last) {
        this.first = first;
        this.last = last;
    };
});

define("main", ["employee"], function (employee) {
    var john = new employee.Employee("John", "Smith");
});
2

2 Answers

114
votes

The AMD proposal allows you to just return a value for the exported object. But note that is a feature of the AMD proposal, it is just an API proposal, and will make it harder to translate the module back to a regular CommonJS module. I think that is OK, but useful info to know.

So you can do the following:

I prefer modules that export a constructor function to start with an upper-case name, so the non-optimized version of this module would also be in Employee.js

define("Employee", function () {
    //You can name this function here,
    //which can help in debuggers but
    //has no impact on the module name.
    return function Employee(first, last) {
        this.first = first; 
        this.last = last;
    };
});

Now in another module, you can use the Employee module like so:

define("main", ["Employee"], function (Employee) {
    var john = new Employee("John", "Smith");
});
106
votes

As an addition to jrburke's answer, note that you don't have to return the constructor function directly. For most useful classes you'll also want to add methods via the prototype, which you can do like this:

define('Employee', function() {
    // Start with the constructor
    function Employee(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    // Now add methods
    Employee.prototype.fullName = function() {
        return this.firstName + ' ' + this.lastName;
    };

    // etc.

    // And now return the constructor function
    return Employee;
});

In fact this is exactly the pattern shown in this example at requirejs.org.