Tweet

modularThe importance of decoupling applications

When we call an application modular, we usually mean that it consists of a highly decoupled set of unique functions housed in different modules. As you probably already know, loose coupling makes maintainability easier by removing as many dependencies as possible. When this is effectively done, it becomes obvious how changes in one part of the system will affect other parts.

However, unlike some more traditional programming languages, the current version of JavaScript (ECMA-262) does not provide developers with a way to introduce modules in a concise, methodical way. One of the problems with specifications is that they don’t give it enough thought. It wasn’t until recent years that the need for more organized JavaScript applications became apparent.

Instead, current developers are forced to relegate themselves to various variations of the module pattern or object literal pattern. In many of these ways, modules’ scripts are strung together and injected into the DOM, whose namespace is described by a single global object. Your entire architecture is still likely to have naming conflicts in this mode. It is often not possible to manage dependencies succinctly without some manual processing or third-party libraries.

While native solutions to these problems will only be introduced in ES Harmony, the good news is that writing modular JavaScript is now extremely simple and can be started today.

In this article, we’ll look at three formats for writing modular JavaScript: AMD, CommonJS, and the proposal in the next version of JavaScript, Harmony.

The prefaceComments about the script loader

To discuss AMD and CommonJS modules, we must cover one obvious topic — script loaders. Currently, script loading serves the goal of enabling modular JavaScript to be used in all kinds of applications today. To do this, using a compatible script loader has to be a must. To make this article easier to read, I recommend that you take a look at how the major script-loading tools work, so that the discussion of module formats here will be more meaningful.

There are many loaders that handle module loading in AMD and CJS formats, but my personal favorites are RequireJS and curl. A full introduction to these tools is beyond the scope of this article, but I recommend reading John Hann’s article on curl. Js and James Burke’s RequireJS API documentation to learn more.

From a production perspective, it is recommended to use optimization tools (such as RequireJS Optimizer) to assemble scripts for deployment when dealing with such modules. Interestingly, with Almond AMD Shim, RequireJS doesn’t even need to be added to a deployed site, and the thing you use as a script loader can be easily removed from the development process.

Still, James Burke would probably say that there are still use scenarios where scripts can be dynamically loaded after a page is loaded, and RequireJS has a place in those scenarios. With those notes, let’s get started.

AMD A format for writing modular JavaScript in a browser

The overall goal of the AMD (Asynchronous Module Definition) format is to provide a usable modular JavaScript solution for today’s developers. It was born out of Dojo’s hands-on experience with XHR+ Eval, and its proponents hope that future solutions will be free from the pitfalls of past solutions.

The AMD module format itself is a proposal on how to define a module in which modules and dependencies can be loaded asynchronously. It has a number of unique advantages, including natural asynchrony and a high degree of flexibility that can remove the tight coupling between common code and module identifiers. It is favored by many developers and can be considered a solid building block toward the modular system proposed in ES Harmony.

AMD originally started as a draft specification for the module format in the CommonJS list, but since there was no complete consensus, the subsequent development of the format was moved to the AMDJS discussion group.

It has been embraced by projects including Dojo (1.7), MooTools (2.0), Firebug (1.8), and even jQuery (1.7). Although we still come across the CommonJS AMD format from time to time, since not all participants on the CJS list are willing to support it, we are better off calling it AMD or asynchronous module support.

Note:

Start with modules

You need to be familiar with two important concepts: the DEFINE method for module definition and the require method for dependency loading. Define defines named or anonymous modules according to the following method signature:

Define (module_id /* optional */, [dependencies] /* optional */, definition function /* to initialize modules or objects */);Copy the code

As you can see in the comments, module_id is an optional parameter that must normally only be given when using non-AMD splicing tools (although it may be useful in some extreme cases). When this parameter is omitted, we say that the module is anonymous.

When anonymous modules are used, it is easy to avoid duplication of file names or code because such modules are DRY. Because such code is more portable, it can be easily moved elsewhere (or moved around the file system) without changing the code itself or its ID. Module_id acts like a file path in a simple package and is not even used in some packages. By using an AMD optimizer that runs in a CommonJS environment such as R.js, developers can run the same code in multiple different environments.

Returning to the defined method signature, the Dependencies parameter represents a set of dependencies that are required for the defined module. The third argument (‘ Definition function’) is a function used to perform initialization for your module. The simplest module can be defined as follows:

Understanding AMD: Define ()

// Here module_id (myModule) is used only as an example define('myModule', ['foo', 'bar'], // Module defines functions // Dependencies (foo and bar) are mapped to function arguments (foo, Var myModule = {doStuff:function(){console.log('Yay! '){var myModule = {doStuff:function(){console.log('Yay! Stuff'); } } return myModule; }); // Another example could be... Define ('myModule', ['math', 'graph'], function (math, graph) {// Return {plot: function(x, y){return graph.drawpie (math.randomGrid(x,y)); }}}; });Copy the code

Require, on the other hand, is primarily used to load code in top-level JavaScript files or when dependencies need to be read dynamically. An example of usage is as follows:

AMD: require()

// Assume that 'foo' and 'bar' are two external modules // In this case, The 'exports' of these two modules are passed as two arguments to the callback function, so they can be accessed like this: require(['foo', 'bar'], function (foo, Bar) {// Write the rest of the code foo.dosomething (); });Copy the code

Dynamically loaded dependencies

define(function ( require ) { var isReady = false, foobar; Function (foo, bar) {isReady = true; function (foo, bar) {isReady = true; foobar = foo() + bar(); }); Return {isReady: isReady, foobar: foobar}; });Copy the code

Understand AMD: plug-ins

The following example defines an AMD-compatible plug-in:

// With AMD, it's possible to load in assets of almost any kind // including text-files and HTML. This enables us to have template // < span class = "max-class: 100%" style = "box-sizing: border-box; line-height: 20px; This includes text files and HTML. This lets us // also dynamically get template dependencies available for skin components at page load time and thereafter. define(['./templates', 'text!./template.md','css! ./template.css'], function( templates, template ){ console.log(templates); // Do some interesting template processing here}});Copy the code

Note:

Load the AMD module with require.js

Var module = new myModule(); require(['app/myModule'], function(myModule){var module = new myModule(); module.doStuff(); });Copy the code

Use curl. Js to load AMD modules

Curl (['app/ mymodule.js '], function(myModule){var module = new myModule(); module.doStuff(); });Copy the code

Modules that contain dependencies that require lazy loading

// This is compatible with jQuery's Deferred implementation, future.js (with slightly different syntax), or a variety of other implementations define(['lib/Deferred'], function( Deferred ){ var defer = new Deferred(); require(['lib/templates/?index.html','lib/data/?stats'], function( template, data ){ defer.resolve({ template: template, data:data }); }); return defer.promise(); });Copy the code

Why is AMD a better choice for writing modular JavaScript?

  • Provides a clear solution for defining highly flexible modules.
  • Add injection to the global namespace that many of us use today<script>The tag solution is much cleaner. It has a concise way to declare individual modules and the dependencies they may contain.
  • Module definitions are encapsulated, helping us avoid global namespace contamination.
  • It works better than other alternatives, such as CommonJS, which we’ll discuss in a moment. There are no cross-domain, local, or debugging issues, and no dependence on server-side tools. Most AMD loaders support loading modules in a browser without a build process.
  • Provides a “transfer” method for including multiple modules in a single file. Other approaches such as CommonJS do not yet have a consensus on the transport format.
  • Lazy loading can be done when needed.

reading

The RequireJS Guide To AMD

What’s the fastest way to load AMD modules?

AMD vs. CJS, what’s the better format?

AMD Is Better For The Web Than CommonJS Modules

The Future Is Modules Not Frameworks

AMD No Longer A CommonJS Specification

On Inventing JavaScript Module Formats And Script Loaders

The AMD Mailing List

AMD modules under Dojo

Defining AMD-compatible modules using Dojo is fairly straightforward. As mentioned earlier, each module dependency is defined in an array as the first argument, and then provides a callback (factory) function that executes after the dependency is loaded, such as:

Define (["dijit/Tooltip"], function(Tooltip){// New Tooltip(...) ; });Copy the code

Note that the natural anonymity of this module enables it to be used by conventional module loaders such as a Dojo asynchronous loader, Require JS, or the standard dojo.require().

There are some interesting pitfalls if you want to learn about module references, and it will be helpful to learn about them here. While AMD advocates referring to modules by declaring them in a dependency list, corresponding to a set of parameters, the Dojo 1.6 build system does not support this approach — this only works in amD-compliant loaders. Such as:

define(["dojo/cookie", "dijit/Tooltip"], function( cookie, Tooltip ){ var cookieValue = cookie("cookieName"); new Tree(...) ; });Copy the code

This has many advantages over nested namespaces because modules no longer need to refer directly to the full namespace each time — all we need is the ‘dojo/cookie’ path in the dependency, and once the alias is generated by the parameter, we can refer to that variable. This eliminates the need to repeatedly type ‘dojo.’ in the application.

Note:

The final pitfall to be aware of is that if you want to continue using Dojo’s build system or want to port older modules to the new AMD style, the more detailed versions below are easier to port. Note that dojo and dijit are also referenced as dependencies:

define(["dojo", "dijit", "dojo/cookie", "dijit/Tooltip"], function(dojo, dijit){ var cookieValue = dojo.cookie("cookieName"); new dijit.Tooltip(...) ; });Copy the code

AMD Module Design Pattern (Dojo)

If you’ve followed any of my previous articles on the benefits of design patterns, you know that they can be very effective in improving the way we explore structural solutions. John Hann recently gave a presentation on AMD module design patterns, covering Singleton, Decorator, Mediator, and more. I highly recommend checking out his slides if you get the chance.

Here are a few examples of these patterns:

Decorator pattern:

/ / mylib/UpdatableObservable: Dojo /store/Observable decorator define(['dojo', 'dojo/store/Observable'], function (dojo, Observable ) { return function UpdatableObservable ( store ) { var observable = dojo.isFunction(store.notify) ? store : new Observable(store); observable.updated = function( object ) { dojo.when(object, function ( itemOrArray ) { dojo.forEach( [].concat(itemOrArray), this.notify, this ); }; }; return observable; // make 'new' optional}; }); / / decorator user / / mylib UpdatableObservable a user define ([' mylib/UpdatableObservable], function ( makeUpdatable ) { var observable, updatable, someItem; / /... Gets or gets the code for an 'Observable' //... Make the Observable Store updatable as well. Updatable = makeUpdatable(Observable); // `new` is optional! / /... Later, when a COMetd message arrives with a new data item, updatable. Updated (updatedItem); });Copy the code

Adapter mode

// 'mylib/Array' ADAPTS the 'each' function to a jQuery interface: define(['dojo/_base/lang', 'dojo/_base/array'], function (lang, array) { return lang.delegate(array, { each: function (arr, lambda) { array.forEach(arr, function (item, i) { lambda.call(item, i, item); // like jQuery each})}}); }); // adapter user // 'myapp/my-module': define(['mylib/Array'], function ( array ) { array.each(['uno', 'dos', 'tres'], function (i, Esp) {// here 'this' == item}); });Copy the code

AMD module under jQuery

basis

Unlike Dojo, jQuery really comes from a single file. However, because of the plugin-based nature of its class library, we can still show how straightforward it is to define an AMD module that uses it.

define(['js/jquery.js','js/jquery.color.js','js/underscore.js'], function($, colorPlugin, Here we pass in jQuery, color plug-ins, and Underscore // We cannot access any of them in global scope, but we can easily reference them below // Var shuffleColor = _.shuffle(['#666','#333','#111']); $('.item'). Animate ({'backgroundColor': shuffleColor }); return {}; // We return something that can be used by other modules});Copy the code

But there is something missing in this example, and that is the concept of registration.

Register jQuery as an asynchronous compatible module

One of the key features added to jQuery 1.7 is the support for registering jQuery as an asynchronous module. There are many compatible script loaders (including RequireJS and curl) that can load modules in an asynchronous module format, which means you don’t need too many hacks to get things up and running.

Due to the popularity of jQuery, various AMD loaders need to consider loading multiple versions of the library on the same page, and ideally you don’t want several different versions loading at the same time. The loader either considers this issue specifically or alerts the user to known problems with third-party scripts and their libraries.

What’s new with version 1.7 is that it helps prevent other third-party code on the same page from accidentally loading a version of jQuery that the author didn’t expect. You don’t want your content to be corrupted by other instances, so this is good.

The way it works is that the script loader used indicates that it supports multiple versions of jQuery by specifying a property, define.amd.jQuery to true. If you are interested in the specific implementation details, you can register jQuery as a named module because there is a risk that it will be pieced together with other files that use AMD define() methods without a proper piecing script that understands anonymous AMD module definitions.

AMD, by name, provides a robust and secure layer of protection for most use cases.

// create multiple global instances of jQuery So that the test of noConflict () var jQuery = this. JQuery | | "jQuery", $= this. $| | "$", originaljQuery = jQuery, the original $= $, amdDefined; define(['jquery'] , function ($) { $('.items').css('background','green'); return function () {}; }); Define. AMD = {jQuery: true};Copy the code

More intelligent jQuery plug-in

I recently discussed here some ideas and examples of how to write jQuery using UMD (Universal Module Definition). UMD defines modules that work on both the client and server side and work with the major script loaders currently available. While this is still a new area where many concepts have yet to be finalized, take a look at the code examples in the section titled AMD vs. CommonJS. If there’s anything you think we can do better, let me know.

Which script loaders and frameworks support AMD?

Browser side:
  • RequireJS requirejs.org
  • The curl. Js github.com/unscriptabl…
  • bdLoad bdframework.com/bdLoad
  • Yabble github.com/jbrantly/ya…
  • PINF github.com/pinf/loader…
  • (And more)
Server side:
  • RequireJS requirejs.org
  • PINF github.com/pinf/loader…

AMD summary

The above are just simple examples of how useful AMD modules can be, but I expect they will provide a foundation for understanding how AMD modules work.

It may be of interest to you if you know that many of the large applications or companies we see are using AMD modules as part of their infrastructure. These include IBM and BBC iPlayer, which clearly shows how serious enterprise developers are about the format.

To learn more about why so many developers choose to use AMD modules in their apps, you might want to take a look at this article by James Burke.

CommonJS Module format optimized for server side

CommonJS is a voluntary working group that designs, plans, and standardizes JavaScript apis. So far they have tried to endorse module standards as well as package standards. The CommonJS module proposal specifies a simple API for declaring modules on the server side. Unlike AMD, it tries to cover broader areas such as IO, file systems, the Promise pattern, and so on.

An introduction to

From a structural point of view, a CJS module is a reusable piece of JavaScript that exports a specific set of objects to be called by code that depends on it — typically such a module has no functions wrapped around it (so you won’t see define in this example).

At a higher level, they contain two main parts: a free variable called exports that contains objects a module wishes to provide to other modules; And a require function that allows modules to import exports from other modules.

Understand CJS: require() and exports

// package/lib is a dependency we need var lib = require('package/lib'); Function foo(){lib.log(' Hello world! '); } // export foo to other module exports.foo = foo;Copy the code

The basic usage of exports

Function foobar(){this.foo = function(){console.log('Hello foo'); } this.bar = function(){ console.log('Hello bar'); } // exports foobar to other modules. Foobar = foobar; Var foobar = require('./foobar').foobar, test = new foobar(); test.bar(); // 'Hello bar'Copy the code

The AMD equivalent of the first CJS example

Define (['package/lib'], function(lib){function foo(){lib.log(' Hello world! '); } // Export (expose) foo to other modules return {foobar: foo}; });Copy the code

Use multiple dependencies

app.js
var modA = require('./foo'); var modB = require('./bar'); exports.app = function(){ console.log('Im an application! '); } exports.foo = function(){ return modA.helloWorld(); }Copy the code
bar.js
exports.name = 'bar';Copy the code
foo.js
require('./bar');
exports.helloWorld = function(){
    return 'Hello World!!''
}Copy the code

Which loaders and frameworks support CJS?

Browser side:
  • The curl. Js github.com/unscriptabl…
  • It is 1.1 sproutcore.com
  • PINF github.com/pinf/loader…
  • (And more)
Server side:
  • Nodenodejs.org
  • The Narwhal github.com/tlrobinson/…
  • Perseverewww.persvr.org/
  • Wakandawww.wakandasoft.com/

Does CJS work in browsers?

Some developers feel that CommonJS is better suited for server-side development, which goes some way to explaining why, in the current pre-Harmony era, there was a degree of disagreement over which format was more appropriate as a de facto standard. The arguments against CJS include that many CommonJS apis that deal with server-side features simply cannot be implemented in JavaScript at the browser level — such as IO, System, and JS because of their inherent features.

Nonetheless, it is useful to know how to structure CJS modules so that when we define modules that are likely to be used anywhere, we have a better sense of their suitability. Modules that have applications on both the client and server side include validation, transformation, and template engines. Some developers choose which format to use by choosing CJS when a module is available in a server-side environment and AMD otherwise.

Because AMD modules can use plug-ins and can define finer grained things like constructors and functions, it makes sense to use AMD. CJS modules can only define objects. Dealing with such modules becomes tedious when you try to get constructors from them.

Although beyond the scope of this article, you may have noticed that in discussing AMD and CMJ we mentioned different types of ‘require’ methods.

One worry about similar naming conventions, of course, is that they can lead to confusion. And the community is currently divided on the advantages and disadvantages of using a global require function. John Hann suggests that instead of calling it ‘require’ and probably failing to make the user understand the difference between global and inner require, it would make more sense to rename the global loader method to something else (such as the name of the class library). This is why loaders like curl. Js use curl() instead of require.

Related Reading

Demystifying CommonJS Modules

JavaScript Growing Up

The RequireJS Notes On CommonJS

Taking Baby Steps With Node.js And CommonJS – Creating Custom Modules

Asynchronous CommonJS Modules for the Browser

The CommonJS Mailing List

AMD and CommonJSCompeting but equally valid standards

Although this article focuses more on AMD than CJS, in fact both formats are valid and both have their uses.

AMD took a browser-first approach to development, opting for asynchronous behavior and simplified backward compatibility, but it had no concept of file I/O at all. It supports objects, functions, constructors, strings, JSON, and many other types of modules that run in the browser native environment. It’s incredibly flexible.

CommonJS takes a server-first approach, with synchronous behavior and no global baggage like John Hann. John Hann should have said globally that all modules need to be wrapped in define(), but CJS does not), with an eye to adapting to future scenarios (on the server). This means that CJS supports unwrapped modules, which feel closer to the ES. Next /Harmony specification, freeing you from the DEFINE () wrapping function required by AMD. However, CJS modules only support objects as modules.

Although the idea that there might be another module format is somewhat daunting, you might be interested to see some examples of AMD/CJS hybrid modules and generic AMD/CJS modules working together.

Basic AMD Hybrid Format (John Hann)

define( function (require, exports, module){ var shuffler = require('lib/shuffle'); exports.randomize = function( input ){ return shuffler.shuffle(input); }});Copy the code

AMD/CommonJS Common Module Definitions (Variant 2,UMDjs)

/** * If you need to do some loop dependencies or need to be compatible with non-Node class * commonJS environments, export the object-based version. */ (function (define) {//'id' is optional, but is recommended if this is a popular Web class library and // is usually used in non-AMD /Node environments. However, if you want to generate anonymous modules, remove the following 'ID' and remove the ID used in the define compatibility scheme. Define ('id', function (require, exports) {var a = require('a'); // Bound to exports. exports.name = value; }); }(typeof define === 'function' && define.amd ? define : function (id, factory) { if (typeof exports ! == 'undefined') { //commonjs factory(require, exports); } else {// Create a global function. Only available if the code has no dependencies, or // the dependencies fit the following call pattern. factory(function(value) { return window[value]; }, (window[id] = {})); }}));Copy the code

Extensible UMD plugin (variant modified by Thomas Davis and myself)

core.js
Note: The wrap code you see around modules is designed to allow us to support multiple module formats and specifications. This requires mapping parameters to what is expected of a particular format. Our actual module work // can be defined below, where a named module and export are shown. ; (function (name, definition){var theModule = definition(), // This is considered "safe" : hasDefine = typeof define === 'function' && define.amd, // hasDefine = typeof define === 'function', hasExports = typeof module ! == 'undefined' && module.exports; If (hasDefine){// AMD module define(theModule); } else if (hasExports) {// node.js module module.exports = theModule; } else {/ / assigned to common namespace, or simply assigned to the global object (window) (enclosing the jQuery | | this. Ender | | this. $| | this, [name] = theModule; } })( 'core', function () { var module = this; module.plugins = []; module.highlightColor = "yellow"; module.errorColor = "red"; // This is the core method highlightAll() and the highlight method used by all plug-ins // to highlight elements with different colors module.highlight = Function (el,strColor){// This module uses jQuery, but old plain JavaScript or Dojo // can be easily used. if(this.jQuery){ jQuery(el).css('background', strColor); } } return { highlightAll:function(){ module.highlight('div', module.highlightColor); }}; });Copy the code
myExtension.js
; (function ( name, definition ) { var theModule = definition(), hasDefine = typeof define === 'function', hasExports = typeof module ! == 'undefined' && module.exports; If (hasDefine) {// AMD module define(theModule); } else if (hasExports) {// node.js module module.exports = theModule; } else {// // allocate to common namespace, or simply allocate to global object (window) // for loop to handle flat file/global module extension var obj = null; var namespaces = name.split("."); var scope = (this.jQuery || this.ender || this.$ || this); for (var i = 0; i < namespaces.length; i++) { var packageName = namespaces[i]; if (obj && i == namespaces.length - 1) { obj[packageName] = theModule; } else if (typeof scope[packageName] === "undefined") { scope[packageName] = {}; } obj = scope[packageName]; }}})('core.plugin', function () {// Define your module here and return the public API // This code can easily be adapted to core code to allow methods that override/extend core // functionality, Allows you to extend the Highlight method to do more as needed. return { setGreen: function ( el ) { highlight(el, 'green'); }, setRed: function ( el ) { highlight(el, errorColor); }}; });Copy the code
app.js
$(function(){// the 'core' plugin is exposed in a namespace called core. // then use some native core functionality to highlightAll div core.highlightall () on the page in yellow; // Set the first div in the page to a green background. core.plugin.setGreen("div:first"); // Here we use core's 'highlight' method from the bottom through a plugin loaded after it // set the background color of the last div to the color of the // 'errorColor' attribute we defined in the core module/plugin. If you look at the code more closely, you'll see how easy it is to use properties and methods between core and other plugins. Core.plugin.setred ('div:last'); });Copy the code

ES Harmony Modules of the future

TC39, the standards body responsible for developing ECMAScript syntax and semantics and its future iterations, is made up of a handful of very smart developers. Some of them (Alex Russell, for example) have been watching the evolution of JavaScript usage in large-scale development in recent years and are sensitive to the need for better language features to write more modular JS.

For this reason, a number of exciting additions to the language have been proposed, including flexible modules that can be used on both the client and server sides, a module loader, and more. In this section, I’ll show you some code samples of the syntax in ES. Next to give you a taste of what’s to come. (See this article for details on ES. Next’s relationship with ES Harmony.)

Note:
Traceur
An introduction to
speech

Modules that contain imports and exports

If you’ve finished reading the chapters on AMD and CJS modules, you’re probably already familiar with the concepts of module dependencies (imports) and module exports (or common apis/variables we allow other modules to use). In es.next, these concepts are presented in a more concise way, using the keyword import to specify module dependencies. Export is not that different from what we expect, and I think many developers will understand that immediately when they look at the code below.

  • Import declares that the export of a module is bound as a local variable and can be renamed to avoid naming conflicts.
  • The export declaration declares that the local bindings of a module are externally visible so that other modules can read them but not modify them. Interestingly, modules can export submodules, but not modules already defined elsewhere. You can also rename the exports to make them different from the local names.
Module staff{export var baker = {bake: function(item){console.log('Woo! I just baked ' + item); } } } module skills{ export var specialty = "baking"; export var experience = "5 years"; } module cakeFactory{// specify dependencies import baker from staff; // Import everything with a wildcard import * from skills; export var oven = { makeCupcake: function( toppings ){ baker.bake('cupcake', toppings); }, makeMuffin: function( mSize ){ baker.bake('muffin', size); }}}Copy the code

Remotely loaded modules

The module proposal also applies to remotely stored modules (such as a third-party API wrapper), making it easier to load modules from an external location. Here is an example of pulling the module we defined above:

module cakeFactory from 'http://addyosmani.com/factory/cakes.js';
cakeFactory.oven.makeCupcake('sprinkles');
cakeFactory.oven.makeMuffin('large');Copy the code

Module loader API

The proposed module loader describes a dynamic API for reading modules in a highly controlled context. Loaders support method signatures such as load(URL, moduleInstance, error) for loading modules, and createModule(Object, globalModuleReferences). Here is an example of dynamically loading the module we originally defined. Note that unlike the previous example where we pulled a module remotely, the module loader API is more suitable for dynamic contexts.

Loader.load('http://addyosmani.com/factory/cakes.js',
    function(cakeFactory){
        cakeFactory.oven.makeCupcake('chocolate');
    });Copy the code

CommonJS module for the server class

For server-side developers, the modular system proposed in es.Next is not limited to browser-side modules. In the following example, you can see a proposed cJS-like module for the server:

// io/File.js
export function open(path) { ... };
export function close(hnd) { ... };Copy the code
// compiler/LexicalHandler.js
module file from 'io/File';
 
import { open, close } from file;
export function scan(in) {
    try {
        var h = open(in) ...
    }
    finally { close(h) }
}Copy the code
module lexer from 'compiler/LexicalHandler'; module stdlib from '@std'; / /... scan(cmdline[0]) ...Copy the code

Classes with constructors, getters, and setters

The notation of classes has always been a contentious issue with linguistic purists. What we’ve been using so far is either falling back on the prototype-based nature of JavaScript or essentially desugmenting it with something that lets people define using classes. Desugar, which means to remove syntactic sugar and convert code to a more syntactically rigorous form, is also the framework or abstraction of prototypical behavior.

In Harmony, classes come along with constructors as part of the language and (finally) have some real privacy. In the examples below, I have introduced some inline comments to help you understand how the class is organized, but you may notice that the word “function” is missing. This isn’t a typo: TC39 has been making a conscious effort to reduce our pervasive misuse of the function keyword, hoping that this will help simplify our code.

Class Cake{// We can define the constructor body of a class using the 'constructor' keyword followed by a list of arguments with public and private declarations //. constructor( name, toppings, price, cakeSize ){ public name = name; public cakeSize = cakeSize; public toppings = toppings; private price = price; } // As part of es.next's effort to reduce unnecessary use of function everywhere, you'll see it discarded in usage scenarios like the one below. Here a flag // identifier is followed by a parameter list and a body that defines the new method. addTopping( topping ){ public(this).toppings.push(topping); } // Getters can be defined by declaring a GET before the identifier, method name, and curly brace body. get allToppings(){ return public(this).toppings; } get qualifiesForDiscount(){ return private(this).price > 5; } // Similar to getters, setters can be defined by using the 'set' // keyword before the identifier. set cakeSize( cSize ){ if( cSize < 0 ){ throw new Error('Cake must be a valid size - either small, medium or large'); } public(this).cakeSize = cSize; }}Copy the code

ES Harmony summary

As you can see, ES. Next brings some exciting new stuff. While Traceur could be used to some extent to try out such a feature in the present, keep in mind that planning your system with Harmony might not be a good idea (just not yet). Specification change and potential problems in the aspect of cross-browser will bring risk (for example, Internet explorer 9 May take a long time to die), so before the standard finalized and its coverage is not a problem, you’d better put note in AMD the browser (for running module) and CJS (for servers).

reading

A First Look At The Upcoming JavaScript Modules

David Herman On JavaScript/ES.Next (Video)

ES Harmony Module Proposals

ES Harmony Module Semantics/Structure Rationale

ES Harmony Class Proposals

Conclusion and in-depth readingreview

In this article we looked at several options for writing modular JavaScript in modern modular formats. Compared to only use these format (traditional) module mode has many advantages, including: let the developers avoid created for each module to establish global variables, static and dynamic dependency management have a better support, the script loader has a higher compatibility, the server end module also has a better compatibility (optional), and so on.

In summary, I recommend that you try out what is suggested today, because these formats offer great power and flexibility that can be helpful when building applications based on reusable chunks of functionality.

That’s all for today. If you have further questions about any of today’s topics, feel free to harass me on Twitter and I’ll do my best to help!

The technical review of this article was conducted via Diigo (Google Chrome Edition). Diigo is a free tool that allows you to add comments and highlights to any online document. If you find any bugs or want to suggest an extension, use Diigo (or build gist on GitHub) and I’ll test myself on any ideas you send in.

Copyright 2011 Addy Osmani. All rights reserved.