Classless OOP in JavaScript
The simplest yet elegant way of creating objects in JavaScript is using the object literal. However it has the limitation of making all its members public and often this
is losing context (like in nested functions).
Trying to make the object creation familiar to Java developers, JavaScript initially introduced the Constructor Function. All members of objects created using the Constructor Function are public and these objects are fragile as this
is not referring to the current object in many cases :
- In nested functions
- DOM events
- When declaring a new reference to a method and then executing it
Also the API of these objects is mutable, so the API can be modified from outside the object.
When trying to use the Constructor Function for inheritance the syntax becomes ugly. The new class
syntax offers a much cleaner code but has the same limitations as the Constructor Function:
The Classless OOP paradigm offers a much better way of creating objects by combining closures with an object literal. In this paradigm there will be two kind of objects:
- OOP Objects
- Data Objects
OOP Objects are about:
- Methods. All data will be private, these objects will expose only methods
- Immutable API
- No more “this” loosing context problems
Objects are not data structures. Objects may use data structures; but the manner in which those data structures are used or contained is hidden…Therefore Objects are about functions not about state.
Objects are bags of functions, not bags of data.
Robert Martin
OOP Objects are build using Factory Functions (aka Power Constructor). By removing the self executing part from the Reveling Module Pattern you get a Factory Function.
function Timer(){
return Object.freeze({
});
}
As an example I’m using the Timer Factory Function to build an OOP object. The internal data of the timer it’s private and I can only interact with it using the public API : start()
and stop()
.
function Timer(fn, interval){
“use strict”;
var timerId; function startRecursiveTimer(){ /*code*/ }
function stop(){ /*code*/ }
function start(){ /*code*/ }
return Object.freeze({
start : start,
stop : stop
});
}function print() { /*code*/ }
var timer = Timer(print, 2000);
timer.start();
The memory cost of the Factory Function can be noticed when creating thousands of the same object, but this is not the case as we usually build one or a few instances of OOP Objects per Factory Function.
The memory cost (in Chrome)
+-----------+------------+------------+
| Instances | 10 methods | 20 methods |
+-----------+---------------+---------+
| 10 | 0 | 0 |
| 100 | 0.1Mb | 0.1Mb |
| 1000 | 0.7Mb | 1.4Mb |
| 10000 | 7.3Mb | 14.2Mb |
+-----------+------------+------------+
Data Objects (aka Data Structures):
- Contain only public data
- Are used as input/output from OOP Objects’ methods
- Can be created using an object literal or
Object.create()