68

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");
});
Robert Koritnik
  • 103,639
  • 52
  • 277
  • 404
avernet
  • 30,895
  • 44
  • 126
  • 163

2 Answers2

114

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");
});
Brad Parks
  • 66,836
  • 64
  • 257
  • 336
jrburke
  • 6,776
  • 1
  • 32
  • 23
106

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.

Mark Whitaker
  • 8,465
  • 8
  • 44
  • 68
  • 2
    Hi Mark, your post is exactly what I am looking for. Except one thing. Is it possible to define some fields for Employee object which are not part of constructor? For example have position property and method positionToUpper, but somehow define that property not in constructor employee = new Employee ('john', 'smith'); employee.position = 'manager'; alert(employee.positionToUpper()); – Yaplex Apr 18 '13 at 20:15
  • 7
    Alex, this example was helpful to me, it's very well documented and can probably provide the examples your looking for: https://gist.github.com/jonnyreeves/2474026 – Nathan Prather Jan 14 '14 at 02:58
  • 1
    @NathanPrather that's a great reference -- the comments helped translate me from a java background – Josh Hibschman Feb 11 '15 at 17:19