Last week, I wrote about the difference between CommonJS and ES modules. I said that ES modules don't use objects in the same way that CommonJS modules do. This is true, but it's inaccurate to say that ES modules don't use objects at all.

We could also put the named exports inside curly braces. Although it looks similar, this is not the same thing as an object literal. The functions are still exported individually, not as properties of an object. This is different from CommonJS modules.

To import the named exports individually, we use curly braces. This is not the same thing as destructuring assignment, even though it looks similar. This is because the named exports were exported individually, not as properties of an object. This is different from CommonJS modules.

This is not quite true! When you use a namespace import, i.e. import * as thing from "./thing.js", the exports are available as properties of the namespace object. The default export is available via the .default property.

For example, let's use named and default exports together by combining our examples from last week. We'll export the area() and circumference() functions as named exports and the Circle class as the default export:

export function area(radius) {
return Math.PI * radius ** 2;
}

export function circumference(radius) {
return 2 * Math.PI * radius;
}

export default class Circle {
constructor(radius) {
this.radius = radius;
}

area() {
return Math.PI * this.radius ** 2;
}

circumference() {
return 2 * Math.PI * this.radius;
}
}

Using a namespace import, we can access the area() and circumference() functions via properties with the same name, and the Circle class via the .default property:

import * as circle from "./circle.js";

const radius = 4;

const area = circle.area(radius); // 50.26548245743669
const circumference = circle.circumference(radius); // 25.132741228718345

const myCircle = new circle.default(radius); // Circle { radius: 4 }

Demo: Namespace imports in ES modules.

The namespace object has null prototype, meaning it doesn't inherit from Object.prototype like a “normal” object would. It's also a sealed object. An object is sealed if it is not extensible and if all its properties are non-configurable and therefore not removable (but not necessarily non-writable) (MDN).