How to access the context of imported modules in ES6? - javascript

I might have my terminology mixed up, but in the same manner that I can access the global context through window, I would like to access the current context of my imported modules.
To give a simple example of what I'm doing, imagine we have a file called MyClasses.js which contains the following two classes:
export class MyClass1 {}
export class MyClass2 {}
Then we import said classes into a file called main.js
import {MyClass1, MyClass2} from './MyClasses'
In main.js I might construct a new instance of each class based on some property value.
function main()
{
const config = { case1: 'MyClass1', case2: 'MyClass2', case3: 'MyClass1' };
const myPropValue = 'case3';
const constructorName = config[myPropValue];
const myClass = new context[constructorName](); // MyClass1
}
This is a basic example, but in a situation where there are many classes and cases for constructing such classes, I'd like to map the relationship rather than depend on intricate if/else logic.
One solution would be to attach the imported classes to the window context...
window.MyClass1 = MyClass1;
window.MyClass2 = MyClass2;
... and construct instances of my classes from there:
const myClass = new window[constructorName](); // valid construction
But I'd like to avoid binding these to the global scope. Is there a default context for any imported modules, or do I need to set up a context myself?

Just modify your import and you will be ok:
import * as MyClasses from './MyClasses';

Related

Undertanding JavaScript methods

I am pretty new to JavaScript, coming from a Java background. I was just playing around with the NodeJS ("type": "module") Express framework but got between two types of ways for writing the methods in JS.
Below are the examples (check comments inline).
Type 1:
main.js
const method1 = () => {
...
method2();
...
};
const method2 = () => {
// this is not exported, so it works as a private method and won't be accessible in other JS files
...
};
.
.
.
// likewise there can be many other methods here
export { method1 }; // export other methods as well
Then, I can use the method1 (cannot use method2 as it is not exported) in any other JS file like below:
test.js
import { method1 } from './main.js';
method1();
Type 2:
main.js
class Main {
method1() {
...
method2();
...
}
#method2() {
// this is a private method, so won't be accessible outside of this class
...
}
// likewise other methods here
}
const main = new Main();
export default main;
Then, I can use this class instance in any other JS file like below:
test.js
import main from './main.js';
main.method1();
I want to know what is the difference between these two, when to use which, and which is better.
Both approaches work fine, but Type 2 is somewhat weird because you're using a class only to keep something from being exported.
Classes are usually used to group together data (properties on the instance of the class) with methods that operate on that data. For a silly example:
class NumberMultiplier {
constructor(num) {
this.num = num;
}
multiply(mult) {
return this.num * mult;
}
}
const n = new NumberMultiplier(5);
console.log(n.multiply(10));
Above, there is data (this.num), and there's also a method that operates on the data (multiply).
But in your case, you don't look to have instance data - you only want to group functions together, there's not really a reason to use a class. You can consider defining the functions individually - as you did in the first snippet - or you could use a plain object that gets exported, with only the properties you need:
const method2 = () => {
};
export default {
method1() {
method2();
}
};
If you do have persistent data and want to put it on the class instance, using a class and # private methods is a possibility (creating a single instance with new and then exporting that instance is an example of a singleton).
A potential issue to be aware of is that if you use export default with an object, there's no way to extract a single property of the export when importing in a single line. That is, if you only have a default export, you can't do something like
import { method1 } from './main.js'.default;
You could only do
import theObj from './main.js';
const { method1 } = theObj;
which some would consider to look a bit ugly. Having independent named exports can make it a bit easier for the consumers of a module to import only what they need into standalone identifiers, in a single line.
Classes in JS, unlike your familiarity in Java, are rarely used when not explicitly necessary. Nevertheless, there are situations where OOP in JS could be very useful.
Basically, the first method (Type 1) is what you're going to be using/seeing 99% of the time if you're doing just general JS programming such as front-end websites or apps.
If you're i.e. making a game however, you could use OOP to have better control over the different characters/object in your game.
In terms of back-end or on an infrastructural level, it really depends. You could perfectly use classes (Type 2) if you're following the MVC design pattern, but is again optional.
In the end, it comes down to your own design choice(s). You could go for FP (T1) or OOP (T2) whenever you like in JS, although there are some 'industry standards' for certain scenarios to decide when to use which.
It actually depends on what you are exporting. The type 1 is more appropriate if you export only one or a few objects. With type 1, you can export any primitive type variables or objects and can be used straightaway in the main.js.
However, if you want to export many objects and/or variables, then type 2 makes sense. With type 2, all exports are stored in an object, so you have to access them using this object.
Performance-wise both are same.

Exporting a module object and importing specific properties of said module

I'm writing a JS Module, in which I want to export an object concerning functions declared in the same file.
Ideally, I would like to import only the properties of the exported object, instead of importing the whole object and deconstructing it. Is it possible in some manner?
module.js
export const foo = {
bar: () => console.log("foobar")
}
component.js
import { bar } from './module.js'
bar();
The current problem is that the bar isn't recognized as a function.
I'm writing a JS Module, in which I want to export an object with reference to functions declared on the same file.
There's no reason to have that object. That's what modules are for. The bindings you export become part of a module object (you never get a direct reference to it), so there's no need for you to explicitly create the object — and doing so gets in a the way a bit, and prevents tree-shaking (eliminating code that is never used).
Ideally, I would like to import only properties of the exported object, instead of importing the whole object and deconstructing it.
With your current approach, that's what you have to do, e.g.:
import { foo } from "./module.js";
then to use bar, either use foo.bar(); or destructure:
const { bar } = foo;
bar();
That's because what you've exported isn't bar, it's foo, an object that has a property called bar. Your import should be failing, because there's no named export called foo. (But if you're using a bundler, it may be obscuring that error.)
But again, there's no need for that object. Instead, just do this:
module.js:
export function bar() {
console.log("foobar");
}
or
export const bar = () => {
console.log("foobar");
};
Then your import in component.js will work.

What is the proper way to export a NON-singleton class in NodeJS?

I recently learned that all node modules are cached and behave similar to singletons in most instances.
The problem I am trying to solve is to not have every import result in the same instance being returned. This is probably very simple to figure out however I'm having trouble landing on a solid design pattern as I'm new to Node and ES6.
The goals I'm trying to achieve are:
Private fields
Consumers of the imported module can new up instances
instanceof comparison
The best I was able to come up with is the following:
export default () => {
let _foo = 'bar';
return new class {
get foo() {
return _foo;
}
set foo(value) {
_foo = value;
}
};
};
However this doesn't quite meet all the goals I'm trying to achieve.
Using this method importing modules can't use instanceof to compare prototypes.
It also doesn't matter if importers use the new keyword when creating an instance. Calling let instance = new Module() and let instance = Module() result in the same thing.
I tried to get around this by removing the new keyword from the functions return however this resulted in the importer having to do the following to get a new instance: new (Module())
I have also tried exporting constructor functions but this resulted in the loss of private fields.
What is the proper way to export a constructor function/class from a node module?
UPDATE:
After playing around some more I was able to come up with the following:
const _sFoo = Symbol();
export default class {
constructor() {
this[_sFoo] = 'default';
}
get foo() {
return this[_sFoo];
}
set foo(value) {
this[_sFoo] = value;
}
}
This seems to meet all of my goals however I'm still not sure if this is the best design pattern...
The problem I am trying to solve is to not have every import result in the same instance being returned. This is probably very simple to figure out however I'm having trouble landing on a solid design pattern as I'm new to Node and ES6.
You have a couple options:
You can export the constructor and let the code that is loading your module call that constructor to create their own object. This allows the calling code to create as many independent objects as they desired. Exporting a constructor would require new to be used by the caller unless the constructor explicitly detects they were called without new and then adapts to still return a new instance.
You can export a factory function and let the code that is loading your module call that factory function to create as many of their own objects as they want. The factory function would be just called as a normal function and it would return a new object each time it was called.
You can export a method that, when called, does whatever you want including creating the desired object and returning it (perhaps embedded in an object of other things too). This is just a variant of the factory function, but may include a bunch of things at once.
The goals I'm trying to achieve are:
Private fields
The above do not help you at all with private fields per object. That is a completely separate discussion.
Consumers of the imported module can new up instances
Option 1 above allows the caller to use new directly. The other options are factory functions so they would not use new.
instanceof comparison
You have to export the constructor directly (option 1 above) in order to use instanceof with it. The other options don't export the constructor so you don't have anything to use instanceof with.
What is the proper way to export a constructor function/class from a node module?
You just export the constructor. In Javascript, constructors are just functions so you just export the constructor and then the caller can use let x = new SomeConstructor() to create their own object. They can likewise use if (x instanceof SomeConstructor). With ES6 syntax, you just export the class name and that's equivalent to exporting the constructor.

How to namespace es6 classes (for React components)

This is part ES6 question part React question. I'm trying to use namespaced components in React with ES6 classes and Babel. So I guess the real question is how to name space es6 classes so I can do what is explained here: https://facebook.github.io/react/docs/jsx-in-depth.html#namespaced-components
Since I get an unexpected token error:
class Headline extends Component { ... }
class Headline.Primary extends Component { ...
^
The ECMAScript-6 class declaration syntax expects a standard BindingIdentifer as the class name. A dot is not a valid character inside an identifier name.
In the context used in the link in OP, the "namespace" is an object, and properties are added to that object one by one using the dot notation for property access.
You could replicate that by using a class expression instead:
'use strict'
var ns = {}
ns.MyClass = class {
constructor() {
console.log('in constructor')
}
}
new ns.MyClass()
This doesn't really change with ES6, you still will have to do an assignment:
Headline.Primary = class Primary extends Component { … };
However, using classes like Headline as namespaces is getting pretty deprecated with ES6 (and has previously been a questionable practice anyway), you should instead leverage the new module system. Export Primary as a named export, and instead of importing the Headline class rather do import * as headlines from ….
This link also relates to this question.
In the Module objects section, it is described that you can do something like this:
// headline.js file
export {Headline, Primary}
class Headline {}
class Primary {}
// In another module...
import * as Headline from "headline";
let h = new Headline.Headline();
let hp = new Headline.Primary();
It's not exactly what you are trying to do, but is an alternative.
Another way of doing it is almost like #Bergi has already pointed out, but I'm just clarifying it further:
let Headline = class Headline extends Component { }
Headline.Primary = class Primary extends Component { }
export {Headline as default}
// in another module:
import Headline from 'headline';
let headline = new Headline();
let primary = new Headline.Primary();

How to make Typescript Dojo Widget Newable?

From within an existing Dojo widget, I want to create an instance of some other widget, which has been transpiled from TypeScript. Sounds simple -- but for some reason the transpiled widget is not "newable", unless I specify the classname twice. I can't update all of that existing code, so I need the transpiled widget to be "newable".
Here is the very simple MyTypeScriptWidget.ts:
import _WidgetBase = require("dijit/_WidgetBase");
export class MyTypeScriptWidget extends dijit._WidgetBase
{
constructor(params?: Object, srcNodeRef?: HTMLElement)
{
super(params, srcNodeRef);
}
}
Then, over in the existing Dojo JavaScript (not TypeScript) widget, I want to new up an instance of MyTypeScriptWidget. So, here's what I have to do in MyJavaScriptWidget.js
var myInstance = new MyTypeScriptWidget.MyTypeScriptWidget();
Notice how I have to type it twice? Why? What am I doing wrong? How can I change MyTypeScriptWidget.ts so that MyJavaScriptWidget.ts can use AMD to create an instance like I did before, like this:
define(['dijit/_WidgetBase', 'tool/MyTypeScriptWidget'], function(_WidgetBase, MyTypeScriptWidget) {
return declare([_WidgetBase], {
var myInstance = new MyTypeScriptWidget();
});
});
In TypeScript, export normally defines members on the module's export object. If you want to define a single thing to be exported, you need to use export =.
class MyTypeScriptWidget ... {
...
}
export = MyTypeScriptWidget;

Categories

Resources