{"rowid": 37, "title": "JavaScript Modules the ES6 Way", "contents": "JavaScript admittedly has plenty of flaws, but one of the largest and most prominent is the lack of a module system: a way to split up your application into a series of smaller files that can depend on each other to function correctly. \n\nThis is something nearly all other languages come with out of the box, whether it be Ruby\u2019s require, Python\u2019s import, or any other language you\u2019re familiar with. Even CSS has @import! JavaScript has nothing of that sort, and this has caused problems for application developers as they go from working with small websites to full client-side applications. Let\u2019s be clear: it doesn\u2019t mean the new module system in the upcoming version of JavaScript won\u2019t be useful to you if you\u2019re building smaller websites rather than the next Instagram.\n\nThankfully, the lack of a module system will soon be a problem of the past. The next version of JavaScript, ECMAScript 6, will bring with it a full-featured module and dependency management solution for JavaScript. The bad news is that it won\u2019t be landing in browsers for a while yet \u2013 but the good news is that the specification for the module system and how it will look has been finalised. The even better news is that there are tools available to get it all working in browsers today without too much hassle. In this post I\u2019d like to give you the gift of JS modules and show you the syntax, and how to use them in browsers today. It\u2019s much simpler than you might think.\n\nWhat is ES6?\n\nECMAScript is a scripting language that is standardised by a company called Ecma International. JavaScript is an implementation of ECMAScript. ECMAScript 6 is simply the next version of the ECMAScript standard and, hence, the next version of JavaScript. The spec aims to be fully comfirmed and complete by the end of 2014, with a target initial release date of June 2015. It\u2019s impossible to know when we will have full feature support across the most popular browsers, but already some ES6 features are landing in the latest builds of Chrome and Firefox. You shouldn\u2019t expect to be able to use the new features across browsers without some form of additional tooling or library for a while yet.\n\nThe ES6 module spec\n\nThe ES6 module spec was fully confirmed in July 2014, so all the syntax I will show you in this article is not expected to change. I\u2019ll first show you the syntax and the new APIs being added to the language, and then look at how to use them today. There are two parts to the new module system. The first is the syntax for declaring modules and dependencies in your JS files, and the second is a programmatic API for loading in modules manually. The first is what most people are expected to use most of the time, so it\u2019s what I\u2019ll focus on more.\n\nModule syntax\n\nThe key thing to understand here is that modules have two key components. First, they have dependencies. These are things that the module you are writing depends on to function correctly. For example, if you were building a carousel module that used jQuery, you would say that jQuery is a dependency of your carousel. You import these dependencies into your module, and we\u2019ll see how to do that in a minute. Second, modules have exports. These are the functions or variables that your module exposes publicly to anything that imports it. Using jQuery as the example again, you could say that jQuery exports the $ function. Modules that depend on and hence import jQuery get access to the $ function, because jQuery exports it.\n\nAnother important thing to note is that when I discuss a module, all I really mean is a JavaScript file. There\u2019s no extra syntax to use other than the new ES6 syntax. Once ES6 lands, modules and files will be analogous.\n\nNamed exports\n\nModules can export multiple objects, which can be either plain old variables or JavaScript functions. You denote something to be exported with the export keyword:\n\nexport function double(x) {\n return x + x;\n};\n\n\nYou can also store something in a variable then export it. If you do that, you have to wrap the variable in a set of curly braces.\n\nvar double = function(x) {\n return x + x;\n}\n\nexport { double };\n\nA module can then import the double function like so:\n\nimport { double } from 'mymodule';\ndouble(2); // 4\n\nAgain, curly braces are required around the variable you would like to import. It\u2019s also important to note that from 'mymodule' will look for a file called mymodule.js in the same directory as the file you are requesting the import from. There is no need to add the .js extension.\n\nThe reason for those extra braces is that this syntax lets you export multiple variables:\n\nvar double = function(x) {\n return x + x;\n}\n\nvar square = function(x) {\n return x * x;\n}\n\nexport { double, square }\n\nI personally prefer this syntax over the export function \u2026, but only because it makes it much clearer to me what the module exports. Typically I will have my export {\u2026} line at the bottom of the file, which means I can quickly look in one place to determine what the module is exporting.\n\nA file importing both double and square can do so in just the way you\u2019d expect:\n\nimport { double, square } from 'mymodule';\ndouble(2); // 4\nsquare(3); // 9\n\nWith this approach you can\u2019t easily import an entire module and all its methods. This is by design \u2013 it\u2019s much better and you\u2019re encouraged to import just the functions you need to use.\n\nDefault exports\n\nAlong with named exports, the system also lets a module have a default export. This is useful when you are working with a large library such as jQuery, Underscore, Backbone and others, and just want to import the entire library. A module can define its default export (it can only ever have one default export) like so:\n\nexport default function(x) {\n return x + x;\n}\n\nAnd that can be imported:\n\nimport double from 'mymodule';\ndouble(2); // 4\n\n\nThis time you do not use the curly braces around the name of the object you are importing. Also notice how you can name the import whatever you\u2019d like. Default exports are not named, so you can import them as anything you like:\n\nimport christmas from 'mymodule';\nchristmas(2); // 4\n\nThe above is entirely valid.\n\nAlthough it\u2019s not something that is used too often, a module can have both named exports and a default export, if you wish.\n\nOne of the design goals of the ES6 modules spec was to favour default exports. There are many reasons behind this, and there is a very detailed discussion on the ES Discuss site about it. That said, if you find yourself preferring named exports, that\u2019s fine, and you shouldn\u2019t change that to meet the preferences of those designing the spec.\n\nProgrammatic API\n\nAlong with the syntax above, there is also a new API being added to the language so you can programmatically import modules. It\u2019s pretty rare you would use this, but one obvious example is loading a module conditionally based on some variable or property. You could easily import a polyfill, for example, if the user\u2019s browser didn\u2019t support a feature your app relied on. An example of doing this is:\n\nif(someFeatureNotSupported) {\n System.import('my-polyfill').then(function(myPolyFill) {\n // use the module from here\n });\n}\n\nSystem.import will return a promise, which, if you\u2019re not familiar, you can read about in this excellent article on HTMl5 Rocks by Jake Archibald. A promise basically lets you attach callback functions that are run when the asynchronous operation (in this case, System.import), is complete.\n\nThis programmatic API opens up a lot of possibilities and will also provide hooks to allow you to register callbacks that will run at certain points in the lifetime of a module. Those hooks and that syntax are slightly less set in stone, but when they are confirmed they will provide really useful functionality. For example, you could write code that would run every module that you import through something like JSHint before importing it. In development that would provide you with an easy way to keep your code quality high without having to run a command line watch task.\n\nHow to use it today\n\nIt\u2019s all well and good having this new syntax, but right now it won\u2019t work in any browser \u2013 and it\u2019s not likely to for a long time. Maybe in next year\u2019s 24 ways there will be an article on how you can use ES6 modules with no extra work in the browser, but for now we\u2019re stuck with a bit of extra work.\n\nES6 module transpiler\n\nOne solution is to use the ES6 module transpiler, a compiler that lets you write your JavaScript using the ES6 module syntax (actually a subset of it \u2013 not quite everything is supported, but the main features are) and have it compiled into either CommonJS-style code (CommonJS is the module specification that NodeJS and Browserify use), or into AMD-style code (the spec RequireJS uses). There are also plugins for all the popular build tools, including Grunt and Gulp.\n\nThe advantage of using this transpiler is that if you are already using a tool like RequireJS or Browserify, you can drop the transpiler in, start writing in ES6 and not worry about any additional work to make the code work in the browser, because you should already have that set up already. If you don\u2019t have any system in place for handling modules in the browser, using the transpiler doesn\u2019t really make sense. Remember, all this does is convert ES6 module code into CommonJS- or AMD-compliant JavaScript. It doesn\u2019t do anything to help you get that code running in the browser, but if you have that part sorted it\u2019s a really nice addition to your workflow. If you would like a tutorial on how to do this, I wrote a post back in June 2014 on using ES6 with the ES6 module transpiler.\n\nSystemJS\n\nAnother solution is SystemJS. It\u2019s the best solution in my opinion, particularly if you are starting a new project from scratch, or want to use ES6 modules on a project where you have no current module system in place. SystemJS is a spec-compliant universal module loader: it loads ES6 modules, AMD modules, CommonJS modules, as well as modules that just add a variable to the global scope (window, in the browser).\n\nTo load in ES6 files, SystemJS also depends on two other libraries: the ES6 module loader polyfill; and Traceur. Traceur is best accessed through the bower-traceur package, as the main repository doesn\u2019t have an easy to find downloadable version. The ES6 module load polyfill implements System.import, and lets you load in files using it. Traceur is an ES6-to-ES5 module loader. It takes code written in ES6, the newest version of JavaScript, and transpiles it into ES5, the version of JavaScript widely implemented in browsers. The advantage of this is that you can play with the new features of the language today, even though they are not supported in browsers. The drawback is that you have to run all your files through Traceur every time you save them, but this is easily automated. Additionally, if you use SystemJS, the Traceur compilation is done automatically for you.\n\nAll you need to do to get SystemJS running is to add a \n\nWhen you load the page, app.js will be asynchronously loaded. Within app.js, you can now use ES6 modules. SystemJS will detect that the file is an ES6 file, automatically load Traceur, and compile the file into ES5 so that it works in the browser. It does all this dynamically in the browser, but there are tools to bundle your application in production, so it doesn\u2019t make a lot of requests on the live site. In development though, it makes for a really nice workflow.\n\nWhen working with SystemJS and modules in general, the best approach is to have a main module (in our case app.js) that is the main entry point for your application. app.js should then be responsible for loading all your application\u2019s modules. This forces you to keep your application organised by only loading one file initially, and having the rest dealt with by that file.\n\nSystemJS also provides a workflow for bundling your application together into one file.\n\nConclusion\n\nES6 modules may be at least six months to a year away (if not more) but that doesn\u2019t mean they can\u2019t be used today. Although there is an overhead to using them now \u2013 with the work required to set up SystemJS, the module transpiler, or another solution \u2013 that doesn\u2019t mean it\u2019s not worthwhile. Using any module system in the browser, whether that be RequireJS, Browserify or another alternative, requires extra tooling and libraries to support it, and I would argue that the effort to set up SystemJS is no greater than that required to configure any other tool. It also comes with the extra benefit that when the syntax is supported in browsers, you get a free upgrade. You\u2019ll be able to remove SystemJS and have everything continue to work, backed by the native browser solution.\n\nIf you are starting a new project, I would strongly advocate using ES6 modules. It is a syntax and specification that is not going away at all, and will soon be supported in browsers. Investing time in learning it now will pay off hugely further down the road.\n\nFurther reading\n\nIf you\u2019d like to delve further into ES6 modules (or ES6 generally) and using them today, I recommend the following resources:\n\n\n\tECMAScript 6 modules: the final syntax by Axel Rauschmayer\n\tPractical Workflows for ES6 Modules by Guy Bedford\n\tECMAScript 6 resources for the curious JavaScripter by Addy Osmani\n\tTracking ES6 support by Addy Osmani\n\tES6 Tools List by Addy Osmani\n\tUsing Grunt and the ES6 Module Transpiler by Thomas Boyt\n\tJavaScript Modules and Dependencies with jspm by myself\n\tUsing ES6 Modules Today by Guy Bedford", "year": "2014", "author": "Jack Franklin", "author_slug": "jackfranklin", "published": "2014-12-03T00:00:00+00:00", "url": "https://24ways.org/2014/javascript-modules-the-es6-way/", "topic": "code"}