In implementing the Python module mechanism on top of ES6 modules for the Transcrypt Python to JavaScript compiler, I am faced with the following problem:
There are a large number of standard functions imported from the Python runtime module, like e.g. the Python input function (implemented in JS), which can be made available using named imports (since they shouldn't have to be prefixed with anything in the user code, so input rather than __runtime__.input, to be consistent with Python).
In Python it's allowed to rebind named imports. So I define another function input, which will override the one from the runtime. But if I do so in JS, I get an error:
Identifier 'input' has already been declared
It seems that all imported names are regarded as JS consts, so non-rebindable according to this article. I can think of several clever workarounds, like importing under an alias and then assigning to a module global var rather than const, but like to keep things simple, so my question is:
Am I right that JS named imports are consts, so non-rebindable (and if so, just curious, anyone knows WHY)? Where can I find details on this?
Is there a simple way to circumvent that and still put them in the global namespace of the importing module, but override them at will?
As per the language specification, imported bindings are immutable bindings, so they cannot be changed. The identifiers are reserved as the module gets parsed because of how ES6 modules work: Unlike in Python, imports are not statements that are included as they are executed; instead, all a module’s imports are basically collected during the early compilation and then resolved before the module starts executing.
This makes ES6 modules kind of unsuitable as an implementation for Python’s import system.
As a general way, to avoid losing those names, you can simply give the imported bindings different names. For example an from foo import bar, baz may be compiled to the following:
import { bar as _foo__bar, baz as _foo__baz } from 'foo';
let bar = _foo__bar;
let baz = _foo__baz;
That will only reserve some special names while keeping the bar and baz identifiers mutable.
Another way, which would probably also help you to solve possible import semantics differences would be to simply create a closure:
import { bar, baz } from 'foo';
(function (bar, baz) {
// …
})(bar, baz);
Or even add some other lookup mechanism in between.
Btw. Python’s import is very similar to Node’s require, so it might be worth looking into all those solutions that made Node’s module system work in the browser.
Related
In a regular script a function can be invoked by string name as window["myFunc"]().
Is there an equivalent in a JS script of type="module" at the "top level", apart from declaring an object and assigning a method to it?
Thank you.
No - one of the main benefits of modules is to allow code that avoids that sort of global pollution. The top level of a module works similarly to an IIFE - the module can see everything that's global, but nothing can see what's declared inside the module, except that, also:
Modules can import from other modules
Modules can export to other modules
While you technically can do something like
window.foo = 'foo';
inside a module, writing scripts that use that route defeats the purpose of using a module system at all. Explicit dependencies make code more maintainable.
No, there isn’t an equivalent. Even in a non-module, let, const, and class declarations don’t become properties of the global object or any other object.
(This is a good thing, though! Explicit is better than implicit.)
I'm building a large React application that involves processing lots of data, formatting it and outputting to tables. Occasionally these functions are variables (e.g const x = () => etc.)
I'm storing the functions that do this formatting in Typescript files which I import into my React components.
To give an example, I might write a table formatting function like this:
export const buildMainData = (data: any) => {
do stuff
}
I'm placing it inside a file, called functions.ts (for example).
I then have a React component which makes use of the function.
My question is - is this a bad idea? Am I creating loads of functions that are polluting the memory heap? I'm using create-react-app so I'm not sure if Webpack is doing some magic behind the scenes to prevent global variables, or whether everything I write should be placed inside of React components.
It would be great if anyone with more experience / knowledge in this area could help out. If I'm also completely getting the wrong end of the stick that would also be helpful to know. Thanks.
The variables and functions you're exporting aren't globals, they're exports from the module in which you define them. They're used via import. If you have some that aren't used, modern bundlers like Webpack and Rollup can tree-shake the bundle they create, leaving unused functions out (if there are any). More about tree-shaking in Webpack and in Rollup.js.
It's true that top-level declarations in classic scripts are globals, but top-level declarations in modules are scoped to the module (kind of like the module were a function and you were declaring things inside it), and then possibly exported from it.
I've found at least two ways to import functions in from a module like Ramda for example. There are probably a few more ways to do something very similar like const R = require('ramda');
Option 1 is to import certain functions:
import { cond, T, always, curry, compose } from 'ramda';
Option 2 is to import the whole module like:
import * as R from "ramda";
I would prefer to reference the module from which the function is being called like so:
R.T();
But if the 2nd option is used, does it bring in every Ramda function not just the ones used in a module I'm working in? Are there any impacts on actual memory use, or bandwidth use as far as what gets sent to the browser if option 2 is used?
Is it possible to somehow do this:
// invalid syntax below:
import R { cond, T, always, curry, compose } from 'ramda';
R.T();
My question is kinda related to this one, but it's a bit different
import R (ramda) into typescript .ts file
TL;DR: It does not matter.
import * as … from 'ramda';
import { … } from 'ramda';
will both by default always bring in the complete Ramda module with all its dependencies. All code inside the module would be run, and which syntax was used to reference the exported bindings doesn't matter. Whether you use named or namespaced imports comes down to preference entirely.
What can reduce the file size to download and the used memory is static analysis. After having evaluated the module, the engine can garbage-collect those bindings that are referenced from nowhere. Module namespace objects might make this slightly harder, as anyone with access to the object can access all exports. But still those objects are specified in a way (as immutable) to allow static analysis on their usage and if the only thing you're doing with them is property access with constant names, engines are expected to utilise this fact.
Any size optimisation involves guessing which parts of the module need to be evaluated and which not, and happens in your module bundler (like Rollup or WebPack). This is known as Tree Shaking, dropping parts of the code and entire dependencies when not needed (used by anything that got imported). It should be able to detect which imports you are using regardless of the import style, although it might have to bail out when are doing unusual things with the namespace object (like looping it or using dynamic property access).
To learn about the exact guesses your bundler can make, contact its documentation.
#Bergi is right in his comment, which I think should be the answer. I would also like to point out you can always try things out in Babel to see what it compiles to: click here to see what an example destructuring actually does
So basically even if you destructure just one function from the module, the whole module will be required. In the Babel example I gave, I just extracted Component from the 'react' module, but the compiled code actually just required the whole thing. :)
Adding to #Bergi, Also just for future reference if you want to shed unused functions and import only the desired function, use selective import like below
import isEmpty from 'ramda/src/isEmpty';
This way you can complement it with Webpack and get a better tree shaking. Hope it helps
Disclaimer: I’m a Node.js newbie.
There’s a number of class-based languages in which you can/must use namespaces to organize your code, for example: Java, PHP, ActionScript 3… For a number of those languages, if you choose/have to use namespaces, there’s generally a set of common practices and conventions that govern project organization then:
Classes form the basic code units, and responsibilities are spread across multiple classes.
The class file hierarchy reside in a single top-level directory (most of the time: src/ or lib/).
Each source file contains a single class definition and nothing else.
Each class resides at a specific level of a namespace (or package) hierarchy, which mirrors the filesystem; for example:
in Java: class com.badlogic.gdx.Application would be found in the src/com/badlogic/gdx/Application.java file
in PHP (with PSR-0): class Symfony\Component\HttpKernel\Kernel would be found in the src/Symfony/Component/HttpKernel/Kernel.php file
Foreign class symbols can be imported into the current scope via a specific statement:
in Java: import com.badlogic.gdx.Application;
in PHP: use Symfony\Component\HttpKernel\Kernel;
I’m used to this type of project organization, but I do realize that it’s specific to class/namespace-based languages and that it might not match JavaScript/Node.js’ usual idioms. If I understand the concept of Node.js modules correctly, it’s 1 source file = 1 module, but from what I’ve seen in a lot of NPM packages, a module usually export more than one symbol, and more often than not those exports are functions and not classes/constructors, so it’s pretty different from the conventions described above.
So, I have the following questions:
In JavaScript/Node.js, is it relevant at all to think about distribution of responsibilities in terms of «classes only» (using either the traditional constructor + prototype composition method or the new class shorthand)?
Is the type of project organization described above possible at all in the context of a Node.js project?
In JavaScript/Node.js, is it relevant at all to think about distribution of responsibilities in terms of «classes only» (or «prototypes only» for that matter)?
In Javascript it's a choice rather than a mandate. You can go full OOP even file structure wise. Or just write modules as pure functions. I'd advise you to stick to the structure that's easier for others, who may want to understand your code, to follow. For example, the OOP style:
Let namespace be the path under src
/src/org/xml/XMLDocument.js
and have a class very similar to the popular OOP languages:
// imports
const fs = require('fs');
const XMLNode = require('./XMLNode');
// class def
class XMLDocument extends XMLNode {
// constructor
constructor(filePath){
...
}
// property getter
get filePath(){
...
}
// method
function getElementsByName(name){
...
}
}
// export class to outer world
module.exports = XMLDocument;
Use the class
// import
const XMLDocument = require('./org/xml/XMLDocument');
// create an instance
const doc = new XMLDocument('./mydoc.xml');
So yes, following an OOP structure is relevant when you tackle the problem the OOP way. And there are alternate ways as well.
Another "creator" oriented custom style:
function createXMLDocument(filePath){
const doc = {};
doc._type = "XMLDocument";
... // make the object have XMLDocument features
return doc;
}
function createDXMLDocument(filePath){
const doc = cerateXMLDocument(filePath);
doc._type = "DXMLDocument";
... // modify parent object with DXML features
return doc;
}
You see, there are some patterns the developer adheres to and write all project code in that style.
Is the type of project organization described above possible at all in the context of a Node.js project?
A Node.js project can have any kind of code organisation because of certain features:
Javascript module system is nothing but referencing a js file present somewhere in file system. So there are no special restrictions on file placement. There are modules that are built in, or can be installed via npm.
Module exports can export one or multiple "things" to external world. So a lot of flexibility here as well.
Javascript itself can be easily written in many styles, functional, OOP, procedural etc. It allows developer to modify a lot of Javascript's own nature. Hence possibly "mimic" many programming styles.
In JavaScript/Node.js, is it relevant at all to think about distribution of responsibilities in terms of «classes only» (or «prototypes only» for that matter)?
To be honest I don't really understand this question. You should follow OOP principles if you use classes, but if you do not, you still need to find cohesion between your functions and organize them in modules and folders based on that.
Is the type of code organization described above usual or relevant at all in the context of a Node.js project, and is it technically implementable without too much trouble?
Javascript modules don't have namespaces, which make things a bit easier (Remember that C# and c++ projects usually have a folder structure totally different than the namespaces). Use folders as namespaces and you'll be fine. There is no such rule that you can only have one class per source file. I usually start writing classes and functions in a single file, and reorganize into multiple files when the file grows big. JavaScript's module system is very flexible, you can organize the code literally any way you want.
If not, what are the traditional ways of handling repartition of responsibilities and code reuse in a Node.js project?
The same as anywhere else.
Most languages use 'import' directives to load other module code, like
java -
import a.b.c
elisp -
(load a)
python -
from a import b
But, why does nodejs use a variable expression to load other module functions like
var a = require('a')
i see, most IDEs for javascript like tern.js-emacs, nodeclipse are not able to do source code lookup (for loaded modules) properly because the IDE has to run the code (or) do eval to find out, what properties a loaded module object contains.
You could say JS belongs to a category of languages where the idea that everything is an object on equal footing is part of the "philosophy" that has guided its development. Node's require is a function (an object) supplied by the environment, as is the module object. This pattern is called the Common JS format.
You actually don't have to assign the result of the require function to a variable. It's rare in practice, but the node module you're calling on could just be invoked to cause an action to take place, for example one might require sugar.js which alters some of the native objects but has no methods of its own to offer, so there would be no point in assigning the return value (which is the module.exports object that was supplied during that module's execution).
A more common example of not assigning a module to a variable is when one uses require just to grab some property off the module -- e.g. var x = require('module').methodOfInterest. Similarly, some modules return a constructor, so you may sometimes see var instance = new (require('ConstructorModule'))(options) (which is ugly in my opinion; requires should generally be grouped at the top of a file and acted on only afterwards).
Note: There's really no concrete answer to your question so odds are high that it will get closed as SO-inappropriate.