Testing in Node JS the following modules layout, looks like local exported definitions always replace external exported in case of name collision (see f1 in B.js).
A.js
export const f1 = 'A'
B.js
export * from './A.js'
export const f1 = 'B'
C.js
import * as A from './A.js'
import * as B from './B.js'
console.log(A.f1)
console.log(B.f1)
> node C.js
// A
// B
Is this a rule? I have not found something about how to manage this in Ecmascript specs.
Does import/export order matter?
Do you see this as a reliable method for extending a module overloading functions and/or adding new ones?
Is this a rule? I have not found something about how to manage this in Ecmascript specs.
Yes, Local exports have priority. Which is, in fact, standardized in the spec:
For each ExportEntry Record e in module.[[LocalExportEntries]], do
a. Assert: module provides the direct binding for this export.
b. Append e.[[ExportName]] to exportedNames.
For each ExportEntry Record e in module.[[IndirectExportEntries]], do
a. Assert: module imports a specific binding for this export.
b. Append e.[[ExportName]] to exportedNames.
Specifically the starExport in your case is part of:
For each ExportEntry Record e in module.[[StarExportEntries]], do
(...)
c. Let starNames be requestedModule.GetExportedNames(exportStarSet).
d. For each element n of starNames, do
i. If SameValue(n, "default") is false, then
1. If n is not an element of exportedNames, then
a. Append n to exportedNames.
So, to answer your second question:
Do you see this as a reliable method for extending a module overloading functions and/or adding new ones?
Yes, it's reliable because it's specified in the standard.
Related
When I add a function to the Math object, it does not show inside IntelliSense of VSCode.
Math.dot = function (a,b) {};
If you don't have one already, create a type declaration module file (Ex. index.d.ts) in the root of your project folder, and add to it the following:
declare interface Math {
doc: (a:number, b:number) => number;
}
You can read more about type declaration module files in the official docs at https://www.typescriptlang.org/docs/handbook/declaration-files/templates/module-d-ts.html.
In simpler cases, you can just use JS Doc comments, since a basic subset of JS Doc comments are supported by VS Code's intellisense facilities. As far as I know, this is not one of those supported cases (declaring the type of a function on an existing global object).
This question already has answers here:
Is using an ES6 import to load specific names faster than importing a namespace?
(3 answers)
Closed 4 years ago.
Suppose I have a module foo like this:
export const f = x => x + 1;
export const g = x => x * 2;
I can use this module like this:
import { f, g } from 'foo';
console.log(f(g(2)));
Or like this:
import * as foo from 'foo';
console.log(foo.f(foo.g(2)));
I prefer the second way because it prevents name collisions between modules.
However, is import * less efficient? Does it prevent bundlers (such as Rollup and Webpack) from spotting unused imports and removing them?
When you specify imports as import { f, g } from 'foo'; you guarantee better performance in terms of speed in compilation and bundle size as you will be getting only the dependencies you need.
Notes: as loganfsmyth pointed out, some recent compiler/bundle are able to reference what actually is being used only, this IMO is an additional step which could cost some compilation time (although I did not have time to benchmark this assumption).
import * is less efficient in that you are using more memory to pull the entire library as opposed to just the specific methods that you actually need
Webpack at least (not sure about Rollup), is perfectly able to see that foo.f is a reference to the f exported name, so your two examples will behave the same.
For most bundlers this does not matter since everything has to be included, 'cause
export const g = (() => {
console.log("Side effects!");
return x => x * 2;
})();
I am trying to implement Singleton in JavaScript where I use Node.js module.export and require().
My issue is that module.export so far for me returns new object from a class.
If I make it so that all classes are only available requiring them from Singleton class and not require() - then would I guarantee that I will have one instance only?
Because if all Other classes import from Singleton - how should I import Singleton?
In php no matter how many instances of Singleton you have - each share static properties. So if you make the instance static then all Singleton's no matter in which files they are invoked have the same memory and perform the same action.
However I have no idea what is the case with JavaScript
I also have another approach - If I make let instance variable at the start of the file and check if its empty at the end and if it isn't I make another instance instance = new MyClass() and then module.export = instance. Would this give me the same result?
For example what I currently do
class MyClass {
//some logic here
}
module.export = new MyClass()
What I think of doing but no idea if it is good or it will work
if(!instance){
let instance = 1;
}
class MyClass {
//some logic here
}
if(instance === 1){
instance = new MyClass()
}
module.export = instance
Also to make matter more complicated I have seen several versions of implementations
Here -> https://www.sitepoint.com/javascript-design-patterns-singleton/
There are 3 examples -
1.Old JS ECMA 2015 way with functions
2.ECMA 2016 with object literal instead of class
3.With classes but again Object.freeze and const
Now..these versions make use of Object.freeze which I am not familiar with and const variables...which I am also not perfectly sure how they work.
To recap (TL;DR) - What I want to do is a way so that if I require a class in 10 files with require("MyClass") I won't get 10 instances but only 1.
Does my way work properly and are the examples in the link with Object.freeze() and the use of const good working examples?Do I need an anctual Singleton class to do the job? Because solutions so far don't include one. And If I need - how do I pass that Singleton around so it has only one instance?
Node.js modules are singletons by default.
All imports referring to the same file would refer to the same object in memory. You don't have to do anything special
In JavaScript, specifically in node.js setting, one can spell module.exports = 13; in module.js, then x = import ("module.js"); elsewhere and have 13 assigned to x directly.
This saves some code when a module exports a single function, and I notice a lot of widely used packages (such as through2) make use of it.
Is there a way to do the same in Python? With some black magic, maybe?
I do have heard of a thing called loader that's, I guess, supposed to do some manipulations with a module before making it available. In particular, I think SaltStack makes use of something like that in salt.loader, but the code is too hard for me to follow. I imagine we could write a function similar to this:
def loader(module):
m = __import__(module)
return m["__exports__"]
— Then define __exports__ somewhere in a module we want to import and enjoy functionality very similar to JavaScript's module.exports mechanics. But unfortunately TypeError: 'module' object has no attribute '__getitem__' prevents us from doing that.
Python has importing built in to the language at a more basic level than Javascript does, almost all use cases are covered by a simple import statement.
For your example, all it really boils down to is:
from module import exports as x
So, there's not need to look for code savings by changing module.
The other part of the question is how, as a module author, would you restrict people to seeing only a single symbol.
Generally this is not required except to help users know what are public functions vs implementation details. Python has a few common idioms for this:
Any names that start with a leading underscore, such as _helper, are considered private. They can be accessed as normal, but the implication is you should not.
If a module level variable __all__ = [...] exists, only the strings it contains are considered public. The names must seperatedly be declared in the module.
As well as being documentation, both of these do affect one aspect of the module import:
from module import *
Using a star import is generally discouraged, but only public names will be brought in to the local namespace.
After some thinking I understood that, while we can't say m["__exports__"] due to module object's class not having __getitem__ method, we can still access some of the module's elements with "dot" notation: m.__exports__ works.
Another way: screen all module level names off with an underscore and assign the object to be exported to a variable named after the module, then from module import *.
loader.py:
def loader(module):
m = __import__(module)
return m.__exports__
exports.py:
def _f():
return 13
_a = 31
exports = {"p6": _f, "p8": _a}
__exports__ = exports
Python 2.7:
>>> import loader
>>> e = loader.load ("exports")
>>> e
{'p8': 31, 'p6': <function _f at 0x7fb79d494cf8>}
>>> from exports import *
>>> exports
{'p8': 31, 'p6': <function _f at 0x7fb79d494cf8>}
Python 3:
>>> import loader
>>> e = loader.load ("exports")
>>> e
{'p6': <function _f at 0x7f88ae229ae8>, 'p8': 31}
>>> from exports import *
>>> exports
{'p6': <function _f at 0x7f88ae229ae8>, 'p8': 31}
In the first way proposed, I unfortunately cannot use __all__ in loader.load to filter only listed names from a module being loaded since __getitem__ is not defined for module object.
In the second way proposed I don't get so much control (in that a malicious module can export arbitrary names and manipulate my namespace) and flexibility (in that I cannot assign the module's exported object to arbitrary name anywhere in my code).
So, there is still a bit left to be desired here.
I have variables in app.js:
var G = {};
module.exports = G;
var DATA = G.DATA = 'DATA';
var F1 = G.F1 = function(val)
{
return val;
};
In this manner, I can export variables under the object G, and at the same time, can access the variable directly writing DATA without G. prefix.
So far so good.
Now, I want to run a test for app.js in test.js:
var G = require('./app.js');
console.log(G.DATA); // -> DATA
This works, but I also want to access the variable directly writing DATA without G. prefix like console.log(DATA); // -> DATA
Surely, I could do like
var DATA = G.DATA; for every variables(property) export&required module G object, but obviously it's a tedious process to add every variable to the test file manually to correspond the G objects.
Is there any way to do this automatically?
So far, I'm pessmistic since
JS function encloses var in the own scope, so in theory there's no way to have a helper function to var for every object property.
Thanks.
PS. I would like to avoid any eval or VM of node solution. I have tried them in past, and too much problems.
I could assign a local variables for every property export&required module G object, but obviously it's a tedious process to add every variable to the test file manually to correspond the G objects.
No, that's how it is supposed to work. You - and only you - are in charge of what local variables exist in your module scope. No changes in the "export variables" of an included module should break your code.
Accessing properties on the imported module (with a self-chosen name) is the way to go. This is quite equivalent to Python's import app or import app as g.
If you want some particular properties as local variables, you will usually choose them manually, as in Python's from app import DATA, F1. In JS, you will need a multiple var statement like the one you've shown in your question. However, there is a syntax feature called destructuring assignment which will make this more fluid. You can use this in JavaScript 1.7+ (Gecko), CoffeeScript, or EcmaScript 6:
var {DATA, F1} = require("app.js");
Is there any way to do this automatically?
Yes and No. You should not do this, but you can - just like Python's frowned-upon from app import *. To cite what they say, which is equally true for JavaScript:
[It] introduces an unknown set of names into the interpreter, possibly
hiding some things you have already defined.
Note that in general the practice of importing * from a module or
package is frowned upon, since it often causes poorly readable code.
However, it is okay to use it to save typing in interactive sessions.
In JavaScript, you can[1] use the with-statement:
with (require("app.js")) {
…
}
[1]: Not in ES5.1 strict mode, though - which is recommended for optimisation