Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Igor Irianto
Igor Irianto

Posted on • Edited on • Originally published atirian.to

     

What the heck are CJS, AMD, UMD, and ESM in Javascript?

In the beginning, Javascript did not have a way to import/export modules. This is a problem. Imagine writing your app in just one file - it would be nightmarish!

Then, people much, much smarter than me attempted to add modularity to Javascript. Some of them areCJS, AMD, UMD, and ESM. You may have heard some of them (there are other methods, but these are the big players).

I will cover high-level information: syntax, purpose, and basic behaviors. My goal is to help readers recognize when they see them in the wild 💡.

CJS

CJS is short for CommonJS. Here is what it looks like:

//importing const doSomething = require('./doSomething.js'); //exportingmodule.exports = function doSomething(n) {  // do something}
Enter fullscreen modeExit fullscreen mode
  • Some of you may immediately recognize CJS syntax from node. That's because nodeuses CJS module format.
  • CJS imports module synchronously.
  • You can import from a librarynode_modules or local dir. Either byconst myLocalModule = require('./some/local/file.js') orvar React = require('react'); works.
  • When CJS imports, it will give you acopy of the imported object.
  • CJS will not work in the browser. It will have to be transpiled and bundled.

AMD

AMD stands for Asynchronous Module Definition. Here is a sample code:

define(['dep1', 'dep2'], function (dep1, dep2) {    //Define the module value by returning a value.    return function () {};});
Enter fullscreen modeExit fullscreen mode

or

// "simplified CommonJS wrapping" https://requirejs.org/docs/whyamd.htmldefine(function (require) {    var dep1 = require('dep1'),        dep2 = require('dep2');    return function () {};});
Enter fullscreen modeExit fullscreen mode
  • AMD imports modules asynchronously (hence the name).
  • AMD ismade for frontend (when it was proposed) (while CJS backend).
  • AMD syntax is less intuitive than CJS.I think of AMD as the exact opposite sibling of CJS.

UMD

UMD stands for Universal Module Definition. Here is what it may look like (source):

(function (root, factory) {    if (typeof define === "function" && define.amd) {        define(["jquery", "underscore"], factory);    } else if (typeof exports === "object") {        module.exports = factory(require("jquery"), require("underscore"));    } else {        root.Requester = factory(root.$, root._);    }}(this, function ($, _) {    // this is where I defined my module implementation    var Requester = { // ... };    return Requester;}));
Enter fullscreen modeExit fullscreen mode
  • Works on front and back end (hence the nameuniversal).
  • Unlike CJS or AMD, UMD is more like a pattern to configure several module systems. Checkhere for more patterns.
  • UMD is usually used as a fallback module when using bundler like Rollup/ Webpack

ESM

ESM stands for ES Modules. It is Javascript's proposal to implement astandard module system. I am sure many of you have seen this:

import React from 'react';
Enter fullscreen modeExit fullscreen mode

Other sightings in the wild:

import {foo, bar} from './myLib';...export default function() {  // your Function};export const function1() {...};export const function2() {...};
Enter fullscreen modeExit fullscreen mode
<script type="module">  import {func1} from 'my-lib';  func1();</script>
Enter fullscreen modeExit fullscreen mode

This may not work 100% in all browsers yet (source).

Summary

  • ESM is the best module format thanks to its simple syntax, async nature, and tree-shakeability.
  • UMD works everywhere and usually used as a fallback in case ESM does not work
  • CJS is synchronous and good for back end.
  • AMD is asynchronous and good for front end.

Thanks for reading, devs! In the future, I plan to write in depth about each module, especially ESM because it is packed with many awesomeness. Stay tuned!

Let me know if you notice any errors.

Resources:

Top comments(22)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
ahferroin7 profile image
Austin S. Hemmelgarn
I'm a Systems Reliability and DevOps engineer for Netdata Inc. When not working, I enjoy studying linguistics and history, playing video games, and cooking all kinds of international cuisine.
  • Email
  • Location
    Ohio, United States of America
  • Work
    Site Reliability / DevOps Engineer at Netdata Incorporated
  • Joined

ES6 modules aren't asynchronous, at least not as they are in use right now most places. Put simply, unless you're using dynamic imports (see below), each import statement runs to completion before the next statement starts executing, and the process of fetching (and parsing) the module is part of that. They also don't do selective execution of module code like you seem to imply.import {bar, baz} from './foo.js' loads, parses, and runs all of './foo.js', then binds the exported entities named 'bar', and 'baz' to those names in the local scope, and only then does the next import statement get evaluated. They do, however, cache the results of this execution and do direct binding, so the above line called from separate files will produce multiple references to the single 'bar' and 'baz' entities.

Now, there is a way to make them asynchronous called 'dynamic import'. In essence, you useimport as a function in the global scope, which then returns a Promise that resolves to the module you're importing once it's fetched and parsed. However, dynamic import support is somewhat limited right now (IE will never support it, Edge is only going to get it when they finish the switch to Chromium under the hood, and UC Browser, Opera Mini, and a handful of others still don't have it either), so you can't really use them if you want to be truly portable (especially since static imports (the original ES6 import syntax) are only valid at the top level, so you can't conditionally use them if you don't happen to have dynamic import support).

As a result of this, code built around ES6 modules is often slower than equivalent code built on AMD (or a good UMD syntax).

CollapseExpand
 
iggredible profile image
Igor Irianto
Vim, Rails, cheesy puns
  • Location
    Dallas, TX
  • Joined

Hi Austin, thanks for the reply! I appreciate you taking a lot of time to write this.

  1. According tothis link, it says "ECMAScript 6 gives you the best of both worlds: The synchronous syntax of Node.js plus the asynchronous loading of AMD. ", andthis article also says that " ESM is asynchronously loaded, while CommonJS is synchronous."

  2. Regarding ESM speed, what I meant to say is that ESM creates static module structure (source,source), allowing bundlers to remove unnecessary code. If we remove unnecessary codes using bundlers like webpack/ rollup, wouldn't this allow the shipping of less codes, and if we ship less code, we get faster load time? (btw, just reread the article, I definitely didn't mention rollup usage. Will revise that).

There is a good chance I am wrong (still learning about JS modules) or interpreted what I read incorrectly (also likely, happened before), but based on what I've read, ESM is async and ESM in overall is faster because it removes unnecessary code. I really appreciate your comment - it forced me to look up more stuff and do more research!

CollapseExpand
 
ahferroin7 profile image
Austin S. Hemmelgarn
I'm a Systems Reliability and DevOps engineer for Netdata Inc. When not working, I enjoy studying linguistics and history, playing video games, and cooking all kinds of international cuisine.
  • Email
  • Location
    Ohio, United States of America
  • Work
    Site Reliability / DevOps Engineer at Netdata Incorporated
  • Joined

Digging a bit further myself, I think I know why I misunderstood the sync/async point. Put concretely based on looking further at the ES6 spec, the Node.js implementation of CJS, and the code for Require.js and Alameda):

  • CJS executes imports as it finds them, blocking until they finish.
  • ESM waits to execute any code in a module until all of it's imports have been loaded and parsed, then does the binding/side-effects stuff in the relative order that they happen.
  • AMD also waits to run module code until it's dependencies are loaded and parsed, but it runs each dependency as it's loaded in the order in which they finish loading, instead of the order they're listed in the file.

So, in a way, we're kind of both right. The loading and parsing for ESM modules is indeed asynchronous, but the execution of the code in them is synchronous and serialized based on the order they are imported, while for AMD, even the execution of the code in the modules is asynchronous and based solely on the order they are loaded.

That actually explains why the web app I recently converted from uisng Alameda to ESM took an almost 80% hit to load times, the dependency tree happened to be such that that async execution provided by AMD modules actually significantly cut down on wait times.

CollapseExpand
 
nyngwang profile image
Ning Wang
  • Joined
• Edited on• Edited

This is a notification to express my (many!) thanks for your superb explanation. This resolves all of my confusions about ES6 modules!

CollapseExpand
 
karataev profile image
Eugene Karataev
undefined is not a function
  • Location
    Russia, Novosibirsk
  • Joined

Can you please clarify?

When CJS imports, it will give you a copy of the imported object.

Let's say we have three files:

// obj.jsmodule.exports={a:42};// bar.jsconstfooObj=require('./obj');console.log('obj from bar',fooObj);// index.jsconstobj=require('./obj');obj.a=50;console.log('obj',obj);require('./bar');
Enter fullscreen modeExit fullscreen mode

Console output after runnode index.js:

obj { a: 50 }obj from bar { a: 50 }
Enter fullscreen modeExit fullscreen mode

It's clear thatindex.js andbar.js share the same object fromobj.js.

CollapseExpand
 
mudlabs profile image
Sam
E Pluribus Unum
  • Location
    Australia
  • Joined

Becauseobj.js is exporting an object literal. The first time you requireobj.js the object is instantiated, and assigned memory allocation. Every time you require it, it's the same object, from memory. Ifobj.js was exporting a function that returned an abject, well that would be different.

CollapseExpand
 
karataev profile image
Eugene Karataev
undefined is not a function
  • Location
    Russia, Novosibirsk
  • Joined

If obj.js was exporting a function that returned an abject, well that would be different.

Agree. But in this case the exported function will be the same for all modules (i.e. the function will not be copied for every import).

Thread Thread
 
mudlabs profile image
Sam
E Pluribus Unum
  • Location
    Australia
  • Joined

Right

CollapseExpand
 
almaember1098 profile image
almaember
  • Joined

Not like you should be changing modules anyways...

CollapseExpand
 
roser137 profile image
roser137
  • Joined

Do CJS imports copies the exported object? I think not. If you create an object in a module and export it using module.exports it is used for every import. Imports from other modules do not create new objects. They uses the same object.

CollapseExpand
 
gpingfeng profile image
Gopal
  • Location
    Shenzhen, China
  • Joined

Hello, may I translate your article into Chinese?I would like to share it with more developers in China. I will give the original author and original source.

CollapseExpand
 
iggredible profile image
Igor Irianto
Vim, Rails, cheesy puns
  • Location
    Dallas, TX
  • Joined

Sorry for the late reply, definitely!

CollapseExpand
 
salimkafin profile image
kafin
longlife learner
  • Location
    Jakarta
  • Work
    Fullstack Developer
  • Joined

in this part, which you say can run in browser:

<script type="module">  import {func1} from 'my-lib';  func1();</script>
Enter fullscreen modeExit fullscreen mode

where do it getmy-lib from?

CollapseExpand
 
iggredible profile image
Igor Irianto
Vim, Rails, cheesy puns
  • Location
    Dallas, TX
  • Joined

You can have local file that exportsfunc1, say a js file 'my-lib.js' .

Then your import becomes :

 import {func1} from './my-lib.js';
Enter fullscreen modeExit fullscreen mode

inside my-lib.js, have something like:

export function func1() {  return `Hello func1!`;}
Enter fullscreen modeExit fullscreen mode
CollapseExpand
 
vadorequest profile image
Vadorequest
See Github Profile: https://github.com/Vadorequest/Vadorequest
  • Location
    Lyon, France
  • Work
    CTO at Unly
  • Joined

Another really good article, older, bug goes much more in-depth.

hacks.mozilla.org/2018/03/es-modul...

CollapseExpand
 
iggredible profile image
Igor Irianto
Vim, Rails, cheesy puns
  • Location
    Dallas, TX
  • Joined

This is another good article. Good suggestion!

CollapseExpand
 
samwatts98 profile image
Sam Watts
Java \ TypeScript \ RustBSc CS & Artificial Intelligence. London, UK.He/him.
  • Location
    London
  • Education
    Computer Science & Artificial Intelligence BSc
  • Work
    Full-Stack Developer
  • Joined

Thanks for this explanation! Very concise, well written, and well compared. :)

CollapseExpand
 
masterinquestion profile image
MasterInQuestion
Tend to be more active on GitHub overall.
• Edited on• Edited

    Is writing everything in one file really necessarily nightmarish..?
    Haven't seen enough dependency hell and god-know-how..?
    Much the reason people disfavor GOTO spaghetti.

    Extreme invert. Excess amok.
    Insane decoupling is not applicable: things are by nature coupled, inseparable.
    Elements are each other coupled and constitute a blob of whole, much of unawarely implications.
    .
    Well realizing the underlying implementation is the only way to guarantee effectiveness and efficiency.

CollapseExpand
 
digitalnaut profile image
Cybernaut
Former biochemist-turned-programmer! I'm here :D
  • Location
    Guadalajara, MX
  • Work
    Full Stack Developer at Student/Freelance
  • Joined

Still relevant in 2022 and really cleared things up for me 👏 Thank you!

CollapseExpand
 
fakenickel profile image
FakeNickel
A beginner of learning software devlopment.
  • Email
  • Location
    Shanghai, China
  • Education
    An university in Shanghai.
  • Work
    CEO of a company.
  • Joined

I count myself lucky enough to see the wonderful answer 4 years later here since 2019.

Thanks for the article you shared with us and the discussions indeed sovle my problem.

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Vim, Rails, cheesy puns
  • Location
    Dallas, TX
  • Joined

More fromIgor Irianto

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp