Because closures are a difficult notion…
Overview
A JS object is the equivalent of a Java object with all fields declared as public
let a = {v1: 10, v2: "oqroi"};
console.log(a.v1);
> 10
a.v1 += 1;
console.log(a.v1);
> 11
Missing: private keyword
let a = {v1: 10, v2: "oqroi"};
a.f = function(x) { return x+1;}
a.f(4)
> 5
A function which is a property of an object has the keyword this set to the object.
let a = {v1: 10, v2: "oqroi"};
a.g = function() { return this.v1 = this.v1+1;}
a.g()
> 11
Or written in another way:
let a = {v1: 10, v2: "oqroi", g: function() { return this.v1 = this.v1+1;} }
a.g()
> 11
a.g()
> 12
a.v1
> 12
f1, variable inside of f, is not visible outside
function f() {
let f1 = 4;
console.log(f1);
}
f()
> 4
f1
> ReferenceError
function f() {
let f1 = 4;
return function() { console.log(f1); }
}
let g = f() // f() returns a function referring to the variable f1 inside
f1
> ReferenceError
g()
> 4 // f1 still exists
By creating a function inside another function:
privateThe closure is constructed with:
The closure “is” the set of variables known inside the outside function.
The closure exists as long as the inside function exists == can be accessed.
As soon as the inside function is not referred to by any variable, the function and the closure are garbage-collected.
let g = (function () {
let f1 = 4;
return function() { console.log(f1); }
})(); // the last two parentheses are the call to the anonymous function
f1
> ReferenceError
g()
> 4 // f1 still exists
let h = (function () {
let f1 = 4; // private
let i = {};
i.get = function() { return f1; } // getter
i.set = function(x) { f1 = x; } // setter
return i;
})(); // the last two parentheses are the call to the anonymous function
h.get()
> 4
h.set(10)
> undefined
h.get()
> 10
So we have ways to:
Missing: a way to import stuff
const importer = require("someModuleName");
let h = (function (imp) {
let f1 = imp.foo.bar;
let i = {};
i.get = function() { return f1; } // getter
i.set = function(x) { f1 = x; } // setter
return i;
})(importer);
With this, we have a way to import another set, and rename it if necessary.
To conclude, we have a full module system.
There are two frequent module syntaxes:
A JS file can be considered as a module, if it finishes on something like:
// at the end of toto.js
exports.fibonacciIt = fibonacciIt;
exports.fibonacciRec = fibo_rec;
exports.fibonacciArray = fibonaArr;
exports.fibonacciMap = fibonacciMap;
In that case, in another file, you can use:
let totoMod = require("toto");
console.log(totoMod.fibonacciRec(4));
If the file extension is .cjs, node understands this file to be in CommonJS syntax. If the file extension is .js and the package.json does not have a "type": "module"line, node also understands this file to be in CommonJS syntax.
import {fun} from 'npmmod';import {fun} from './mod';import {fun, fun2, fun1 as foo} from 'mod'; import default from 'mod' mod.fun, mod.fun2, etc.import default as othername from 'mod'export in front of the definitionIf the file extension is .mjs, node understands this file to be in ES6 syntax. If the file extension is .js and the package.json has a "type": "module"line, node also understands this file to be in ES6 syntax.
A classic mistake
function f() {
for (var i=0; i<3; i++) {
setTimeout(function(){ console.log(i);}, 1000);
}
}
f();
// 1s later
3
3
3
This does not work
function f() {
for (var i=0; i<3; i++) {
setTimeout(function(){var j = i; console.log(j);}, 1000);
}
}
f();
// 1s later
3
3
3
There are three variables named j, but they are set later from a reference to the same i.
function g(j) {
return function() { console.log(j); };
}
function f() {
for (i=0; i<3; i++) {
setTimeout(g(i), 1000);
}
}
f();
0
1
2
function f() {
for (i=0; i<3; i++) {
setTimeout((function (j) {console.log(j);})(i), 1000);
}
}
f();
0
1
2