Closures Back
Closures are functions that refer to independent (free) variables, which are usually defined in an enclosing scope. These functions remember the environment in which they were created.
function f() {
var independentVariable = 'test';
function display() {
/** using an independent variable `independentVariable` */
console.log(independentVariable);
}
display();
}
Emulating private property
var counter = (function() {
var privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return privateCounter;
}
};
})();
console.log(counter.value()); // logs 0
counter.increment();
counter.increment();
console.log(counter.value()); // logs 2
counter.decrement();
console.log(counter.value()); // logs 1
A common mistake
<p id="help">Helpful notes will appear here</p>
<p>E-mail: <input type="text" id="email" name="email"></p>
<p>Name: <input type="text" id="name" name="name"></p>
<p>Age: <input type="text" id="age" name="age"></p>
function showHelp(help) {
document.getElementById('help').innerHTML = help;
}
function setupHelp() {
var helpText = [
{'id': 'email', 'help': 'Your e-mail address'},
{'id': 'name', 'help': 'Your full name'},
{'id': 'age', 'help': 'Your age (you must be over 16)'}
];
for (var i = 0; i < helpText.length; i++) {
var item = helpText[i];
document.getElementById(item.id).onfocus = function() {
showHelp(item.help); /**
* will always show the last help
* because three functions have shared the same item
*/
};
}
}
setupHelp();
See the Pen zKYdaZ by aleen42 (@aleen42) on CodePen.
Resolution is that you should return a closure function which will create a separate environment for themselves.
function showHelp(help) {
document.getElementById('help').innerHTML = help;
}
function setupHelp() {
var helpText = [
{'id': 'email', 'help': 'Your e-mail address'},
{'id': 'name', 'help': 'Your full name'},
{'id': 'age', 'help': 'Your age (you must be over 16)'}
];
for (var i = 0; i < helpText.length; i++) {
var item = helpText[i];
document.getElementById(item.id).onfocus = (function(help) {
return function () {
showHelp(help);
};
})(item.help);
}
}
setupHelp();
See the Pen JRjyvQ by aleen42 (@aleen42) on CodePen.
Performance
It's not recommended to define functions inside another functions which will affect the performance of scripts.
function MyObject(name, message) {
this.name = name.toString();
this.message = message.toString();
this.getName = function () {
return this.name;
};
this.getMessage = function () {
return this.message;
};
}
In the above case, what we should do is to use prototype
to define outside:
function MyObject(name, message) {
this.name = name.toString();
this.message = message.toString();
}
MyObject.prototype = {
getName: function () {
return this.name;
},
getMessage: function () {
return this.message;
}
};
But what if the security of property name
and message
is important, what we should do is to give up performance to set name
and message
as private:
function f(name, message) {
var name = name;
var message = message;
this.getName = function () {
return name;
};
this.getMessage = function () {
return message;
};
}