To extend on the JavaScript closure concept, here's an example of how it can be used to build classes with encapsulation. This example has multiple public member functions, and uses the widely accepted syntax common to many languages for accessing class members. The private state of this object is captured in the closure of the factory function, which provides the encapsulation for non-public data (and methods, if desired).
/* Counter with reset */
function generateCounter() {
var count = 0;
function reset() {
count = 0;
}
function next() {
return count++;
}
/* Return an object with two public member functions, which both have implicit access to the same closure for private data */
return {
reset: reset,
next: next
};
}
var counter1 = generateCounter();
var counter2 = generateCounter();
console.log(counter1.next(), counter1.next(), counter1.next()); /* 0 1 2 */
console.log(counter2.next(), counter2.next(), counter2.next()); /* 0 1 2 */
console.log();
/* Reset counter 1 */
counter1.reset();
console.log(counter1.next(), counter1.next()); /* 0 1 */
console.log(counter2.next(), counter2.next()); /* 3 4 */
So yes, closures are sufficient to implement classes and objects in much the way we would expect to use them. I'm not sure I would recommend this way. There are others. Though it is common enough that you might see this from time to time in JavaScript libraries.
I've seen three main ways that people implement class/object behaviour.
- Factory Functions - often combined with the use of closures to capture private state
- Constructor Functions - used with new, and often combined with prototype methods
- ES6/ES2015 class keyword - nice syntax, result is very similar to constructor functions
Looks like I have a few future topics to cover :D
Hmm, I don't think interfaces are particularly relevant for JavaScript. It's all duck-typed and dynamically dispatched. There are no safety/speed issues that would be affected.
Inheritance can still work. You can add or replace methods on the returned object, allowing it to be extended. If one factory function calls another, you can wrap an additional closure around new methods, providing for more private data. (Without new/replaced methods, there would be no reason to capture additional variables). One limitation, is not being able to directly access variables in the old closure.
/* Counter with reset */
function generateCounter() {
var count = 0;
function reset() {
count = 0;
}
function next() {
return count++;
}
/* Return an object with two public member functions, which both have implicit access to the same closure for private data */
return {
reset: reset,
next: next
};
}
function generateEvenCounter() {
var obj = generateCounter();
var oldNext = obj.next;
obj.next = function newNext() {
/*return count += 2;*/ /* No access to old closure, 'count' is not in scope here */
var retVal = oldNext();
oldNext();
return retVal;
}
return obj;
}
var counter = generateEvenCounter();
console.log(counter.next(), counter.next(), counter.next()); /* 0 2 4 */
console.log();
counter.reset();
console.log(counter.next(), counter.next()); /* 0 2 */
Though again, there are Constructor Functions, and the new Class syntax. Probably a better way to express your intent, and I suspect will result in more efficient runtime performance.