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:
private
The 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