Mon Mar 13 2017
Paul Shan
ECMAScript Modules (ESM) are still in stage 3 (not finalized).
require | import |
---|---|
Dynamic evaluation | Static evaluation |
Throws error at runtime | Throws error while parsing |
Non lexical | Lexical |
As Node.js uses commonJS moduling, I will refer to the workflow of node to address the commonJS modules. And will refer the ECMAScript modules as ESM.
dep.js
dep = {
foo: function(){},
bar: 22
}
module.exports = dep;
app.js
var dep = require("dep");
console.log(dep.bar);
dep.foo();
dep.js
export foo function(){};
export const bar = 22;
app.js
import {foo, bar} from "dep";
console.log(bar);
foo();
What Node.js does with modules is, it wraps all the code inside a function scope. So the dep.js
will look like below.
function (exports, require, module, __filename, __dirname) {
const m = 1;
module.exports.m = m;
}
So be it __filename
, __dirname
or module
, they are not actually global but local to that particular module.
The actual loading of any module using require()
happens in 5 steps.
The first step resolution
is an internal step where node.js calculates the file paths etc. In the second one, which is loading
, node pulls the code in the ongoing process. In wrapping
in wraps up the code in the function as shown above and then sends it to VM for evaluating
and then eventually caches
it.
So, basically node never knows what symbols a commonJS module is going to export until and unless the module is actually evaluated. And this is the biggest difference with ECMAScript modules, because ESM is lexical and thus, the exported symbols are known before the code is actually evaluated.
When an ESM module is parsed, then before it is evaluated by the VM, an internal structure called a Module Record
is created. This module record keeps the list of exported symbols at the time of parsing. Thus, when you use import {f} from "foo"
, it actually creates a link between this f
and the f
which is there in module record’s symbols list (in case f is actually exported).
As a result, any error regarding the unavailability of mismatch of any exported symbol will raise an error before the evaluation.
There is a proposal of import()
function too to create nested import statements. Unlike the lexical import
keyword, import()
function is processed at the time or evaluation (more like require). The syntax is like the following.
import("foo").then((module) => {
module.bar();
console.log(module.someProp);
}).catch((err)=>{
//handle error here
});
As import
is still in stage 3 and not implemented by browsers natively, we’re unable to run any performance test. Currently when you use import
in your code, your transpilers transpile it back to require
, the commonJS moduling system. So for the time being both are same.
Though there are no performance benefit at the moment, but I will still suggest to use import
over require
cause it’s going to be native in JS and may (just because it’s native) perform better than require.
SHARE THIS ARTICLE
Thu Mar 10 2016
OAuth authentications are pretty popular now a days and another thing which is popular is JavaScript. This article shows how to plugin google’s oAuth api for authentication in your own node application.Sat Mar 01 2014
This is a continuation of my CSS3 loader snippet collection series. I've provided spinning css3 animation loader in the part 1 of this series and here in part 2, I'm providing various square type loading