Javascript ES6 export const vs export let - javascript

Let's say I have a variable that I want to export. What's the difference between
export const a = 1;
vs
export let a = 1;
I understand the difference between const and let, but when you export them, what are the differences?

In ES6, imports are live read-only views on exported-values. As a result, when you do import a from "somemodule";, you cannot assign to a no matter how you declare a in the module.
However, since imported variables are live views, they do change according to the "raw" exported variable in exports. Consider the following code (borrowed from the reference article below):
//------ lib.js ------
export let counter = 3;
export function incCounter() {
counter++;
}
//------ main1.js ------
import { counter, incCounter } from './lib';
// The imported value `counter` is live
console.log(counter); // 3
incCounter();
console.log(counter); // 4
// The imported value can’t be changed
counter++; // TypeError
As you can see, the difference really lies in lib.js, not main1.js.
To summarize:
You cannot assign to import-ed variables, no matter how you declare the corresponding variables in the module.
The traditional let-vs-const semantics applies to the declared variable in the module.
If the variable is declared const, it cannot be reassigned or rebound in anywhere.
If the variable is declared let, it can only be reassigned in the module (but not the user). If it is changed, the import-ed variable changes accordingly.
Reference:
http://exploringjs.com/es6/ch_modules.html#leanpub-auto-in-es6-imports-are-live-read-only-views-on-exported-values

I think that once you've imported it, the behaviour is the same (in the place your variable will be used outside source file).
The only difference would be if you try to reassign it before the end of this very file.

Related

JavaScript inner lambda function without var, let or const

In the following React-Application, I am using a JS-Feature to declare a function within a function and call it on a certain event:
export default function App() {
OnClick = (input) => { //Why is no var/let needed
//do something...
}
return (<button onClick={this.OnClick}/>); //and it is called like this
}
My resulting questions are:
How is the "feature"/"notation" called? - it seems weird not to use var/let/const
In which ES-Version was it introduced?
Does it only work with Lambda-Functions?
How is the "feature" called? - it seems weard not to use var/let/const
I call it The Horror of Implciit Globals. OnClick gets created as a global variable, not a local one. I don't think it has an official name. A less opinionated one might be "implicit global variable creation." :-)
In wich ES-Version was it introduced?
Before ECMAScript version 1. I think it was in the very first version of JavaScript ever in 1995.
Does it only work with Lambda-Functions?
No, you can do this with any value. OnClick = 42 would also "work."
But don't. Again, it creates a global, not a local. Instead, declare your variables.
Use const for your example (or let if you prefer). I also recommend using JavaScript modules ("ESM" or "ECMAScript" modules), because code in modules is in strict mode and assigning to an undeclared identifier is the error it always should have been in strict mode. If you don't want to use JavaScript modules, add "use strict"; to the top of your files to put them in strict mode.
Here's a demonstration of the fact it creates a global:
function example(value) {
OnClick = () => {
console.log(value);
};
}
example(42);
OnClick(); // Logs 42
console.log(typeof window.OnClick); // "function"

Javascript import all modules from a file as a global variables

In script1.js, I have the following exports:
export function test1() {
// do something
}
export function test2() {
// do something
}
I am importing script1.js into script2.js:
import * as functions from './script1.js';
functions.test1();
functions.test2();
I am wondering if there is a way to import all the modules from script1.js, and directly place them inside the global variables of script2.js. So I can directly access them with test1(), test2(), without the intermediate step of putting them in an object.
I know that we can do import {test1, test2} from './script1.js';, but if I have a lot of exports, then the deconstruction will be tedious.
In another word, what I am looking for is something equivalent of:
import * from './script1.js';
test1();
test2();
The above code block is hypothetical, and it does not work.
Given your current code, it's not possible. The whole point of modules is to avoid implicit global pollution. To do something like this, you'd have to explicitly assign the functions to the global object inside the module, eg, change:
export function test1() {
// do something
}
to
window.test1 = function test1() {
// do something
};
and then, when the module is imported, the functions will be available globally:
import './script1.js';
But this is an antipattern:
but if I have a lot of exports, then the desconstruction will be tedious.
It does require some boilerplate, but don't be afraid of it - in larger projects, having explicit dependency chains is a huge plus for maintainability. It also permits tree-shaking, which isn't possible for modules with side-effects only (like assigning to the global object).
you can also very simply auto generate the import , with a script like this:
import * as functions from './script1.js';
const function_names = Object.keys(functions).join(',')
console.log( function_names ) //output: test1,test2
your list is ready in your console!

Javascript global variable undefined in function scope

I am new to JavaScript and trying to make a simple node server. Here is my code:
var activeGames = {}
exports.initialize = function(){
var gameID = "game12345"
activeGames.gameID = new Game(gameID, "player1", "player2")
}
I call the initialize function from another module, and I get an error stating that activeGames is undefined. activeGames is at the outermost scope of the file. I tried adding 'this' before activeGames.gameID but that did not fix it. Why is activeGames undefined? Thanks in advance.
EDIT: Here's how I'm calling this code.
In my base index file I have
const handler = require("./request-handler.js")
handler.initialize()
In request-handler.js, I have
var gameManager = require('./game-manager')
exports.initialize = function(){
gameManager.initialize()
}
JavaScript has lexical scope, not dynamic scope.
ref: https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scoping
Lexical scope means that whether a variable is accessible or not depends on where they appear in the source text, it doesn't depend on runtime information.
example:
function foo() {
var bar = 42;
baz();
}
function baz() {
console.log(bar); // error because bar is not in the scope of baz
}
the same problem happens in your code,
var activeGames
is not in scope.
try this variation:
exports.initialize = function(){
var activeGames = {}
var gameID = "game12345"
activeGames.gameID = new Game(gameID, "player1", "player2")
}
A good solution could be to use a class and export it:
--THIS CODE IS NOT TESTED--
class gamesManager {
var activeGames = {}
initialize() {
var gameID = "game12345"
activeGames.gameID = new Game(gameID, "player1", "player2")
}
}
exports.gamesManager = gamesManager
USE:
const activeGames = require('./game-manager');
const activeGamesInstance = new activeGames.gamesManager();
activeGamesInstance.initialize();
Need a code sample for this one. I ran this locally and it worked fine, although your code has a big issue which may be a part of your problem. It looks like you want to keep track of multiple games in activeGames. You need to use this syntax instead:
activeGames[gameID] = new Game(gameID, "player1", "player2")
Here's my working code:
index.js:
const handler = require("./request-handler");
handler.initialize('game-1234');
handler.initialize('game-5678');
request-handler.js:
var gameManager = require('./game-manager');
exports.initialize = function(gameID) {
gameManager.initialize(gameID);
}
game-manager.js:
var activeGames = {};
class Game {
constructor(id, player1, player2) {
this.id = id;
this.player1 = player1;
this.player2 = player2;
}
}
exports.initialize = function(gameID) {
activeGames[gameID] = new Game(gameID, "player1", "player2");
console.log(`game initialized! ${ Object.keys(activeGames).length } active games`);
}
Running node index results in this:
game initialized! 1 active games
game initialized! 2 active games
When you require a script file in Node.js, it is compiled as part of a function called with named parameters require, module, exports and other exposed variables as arguments1. Variables declared at file level within the required script become function level variables in the enclosing module wrapper and retained inside its closure.
Hence your "global variable" is no such thing: it's a variable defined inside a closure...
An important question then is does the module loader make variables declared in a parent module available to scripts required inside the parent. A quick test shows that the answer is general: no, modules do not have automatic access to variables declared in other modules - those variables are inside closures.
This indicates that to pass variable values to scripts that have been required, generally pass them as argument values to exported functions.
It is also possible to export any javascript value as a property of module.exports from within a required script, or add properties to an exports object after it has been returned from requiring a script. Hence it is technically feasible to pass information up and down between modules by adding properties to exports objects.
Redesigned code has multiple options to
define activeGames at the application level and pass it down as a parameter to modules needing access to it, or
export activeGames from game-manager.js by adding
exports.activeGames = activeGames
to the end of the file. This will not take care of exporting activeGames out of the parent module request-manager.js for use elsewhere, but it could be a start. Or
define activeGames as a global variable (in node) using
global.activeGames = {} // define a global object
Defining global variables is not encouraged as it can lead to collisions (and consequent program failure) between names used by applications, code libraries, future library updates and future versions of ECMAScript. Or,
Define an application namespace object for data global to the application. Require it wherever access to application data is needed:
create appdata.js as an empty file.
Optionally include a comment:
// this files exports module.exports without modification
require appdata.js wherever needed.
var appData = require('./appdata.js')
appData.gameData = {}; // for example
This relies on node.js maintaining a cache of previously required modules and does not recompile modules simply because they have been required a second time. Instead it returns the exports object of the previous require.
Happy festive season.
References
1The Node.js Way - How require() Actually Works

Are export const statements visible inside the file they reside?

Are export const statements visible inside the file they reside?
export const MAX_NAME_LENGTH = 20;
console.log(MAX_NAME_LENGTH); // can I do this?
Yes, they are. The declaration is still a declaration in the scope in which it appears (e.g., the module's scope, since export can only appear there) and all the usual rules about the declaration apply; all export does is export it.
In that specific case, since it's a const declaration, the "usual rules" include the temporal dead zone above the declaration in which MAX_NAME_LENGTH cannot be used. If you'd exported a function declaration like this:
export function foo() {
}
...the "usual rules" mean you could use foo anywhere in your module code, because it's still hoisted. (Same for export var x;.)
(You can also test things like this with an up-to-date version of Chrome, which now supports modules. :-) )

Convert closure to es6 module

I'm using a javascript build environment that supports es6 modules (using es6-module-transpiler) so you can simply import stuff across different files.
Now I got a third party library that I'd like to be "importable".
The library populates its functionality like this:
(function () {/*...*/}).call(this);
Would it be safe to omit the closure and convert it to:
export default function () {/* ... */};
Or is there a better way?
Thanks in advance!
The original code you show invokes the anonymous function, which to make any sense must define a global variable, whereas the second code fragment you show merely exports the function, which is a different thing.
For purposes of discussion, let's assume the original code defines a global like this:
// my-third-party-module.js
(function() {
let myVar = 22;
window.MyThirdPartyModule = { log: function() { console.log(myVar); } };
}.call(this);
and you are using is as so:
// app.js
MyThirdPartyModule.log();
You could rewrite this as
// my-third-party-module.js
let myVar = 22;
export default { log: function() { console.log(myVar); } };
// app.js
import MyThirdPartyModule from `my-third-party-module';
MyThirdPartyModule.log();
Note that we have moved the variable myVar which was local to the anonymous function to the top module level.
However, depending on your preferences, rather than exporting a big object, which is sort of a pre-module mentality, you might want to export its APIs individually:
// my-third-party-module.js
let myVar = 22;
export function log { console.log(myVar); }
// app.js
import {log} from `my-third-party-module';
log();
or if you prefer
// app.js
import * as MyThirdPartyModule from `my-third-party-module';
MyThirdPartyModule.log();
However, all of these approaches assume you are able and willing to edit the source of the third party library. If that is not the case, you could write a little piece of glue code, such as
// my-third-party-module-interface.js
import 'my-third-party-module'; // This will run the module.
export default MyThirdPartyModule; // Export the global it defined.
// app.js
import MyThirdPartyModule from 'my-third-party-module-interface';
If you would prefer again to export individual APIs, you could extend the glue to re-export each of them:
// my-third-party-module-interface.js
import 'my-third-party-module'; // This will run the module.
const {log, otherAPI, ...} = MyThirdPartyModule;
export {log, otherAPI, ...};
// app.js
import {log} from 'my-third-party-module-interface';
The conversion of legacy dependencies is still an issue. And the horrible workflow they use makes things a lot harder, prefixing the actual code with browserify and webpack silliness.
So what to do? Existentially, the library is guaranteed only to deposit a global in window but by obscure and weird ways. And all slightly different.
So let the legacy simply do what it is supposed to do for you, but wrapped in a module so that you can import it rather than use a script tag:
https://medium.com/#backspaces/es6-modules-part-2-libs-wrap-em-up-8715e116d690

Categories

Resources