Class Free Object Oriented JS-code rocks
After having coded JS for 30 years (from its inception in 1995), the last couple of years my JS-code is almost exclusively Class Free Object Oriented (CFOO, actually the revealing module pattern).
CFOO code is really elegant and a relative simple, robust way to create (constructor) functions (encapsulating private variables) without the need for this
, class
syntax (aka class sugar), prototype
or the new
keyword.
I'd like to share the idea here.
The functions you create with CFOO are factory functions, which return (frozen) Objects (an instance) or (constructor) functions.
Factory functions may encapsulate other factories. Member privacy is guaranteed via closures.
A factory function can look like this:
function myAdditionFactory(n = 1) {
let initial = n;
/*
[n] and [initial] are private
the value of [n] can only be changed/retrieved
using the returned instance
*/
return {
add(value) { n += value},
reset() { n = initial; },
get value() { return n; }
};
}
// assign an instance
const myAdder = myAdditionFactory();
// add some values
for (let i = 0; i < 10; i += 1) {
myAdder.add(i);
}
console.log(myAdder.value); // => 46
myAdder.reset();
console.log(myAdder.value); // => 1
You can encapsulate other 'constructors' in a factory
function myConstructorFactory(otherConstructor) {
/*
the values for the instance to return are
derived from another constructor. Both
[hello] and [world] are private.
This factory returns a factory function
(a 'constructor') using the assigned values
*/
let {hello, world} = otherConstructor();
return function(instanceName) {
const instance = {
get name() { return instanceName; },
toString() {return hello + " " + world; },
setWorld(value) { world = value; return instance; },
setHello(value) { hello = value; return instance; }
// Note: returning the instance makes a method chainable
};
return Object.freeze(instance);
}
}
function myOtherConstructorFactory(hi, wrld) {
return Object.freeze( {
hello: hi || "hello",
world: wrld || "world" } );
}
const helloWorld = myConstructorFactory(myOtherConstructorFactory);
const universe = helloWorld("universe");
console.log(String(universe));
// => 'hello world'
console.log(String(universe.setWorld("universe")));
// => 'hello universe'
console.log(universe
.setHello("Hi")
.setWorld("from the milky way")
.toString());
// => 'Hi from the milky way'
console.log(universe.name);
// => 'universe'
universe.name = "nothing";
universe.instanceName = "nothing";
console.log(universe.name);
// => still 'universe'
console.log(universe.instanceName);
// => undefined
Object.defineProperty(universe, `nothing`, {value: true});
// =>TypeError: Cannot define property nothing, object is not extensible
A more extensive example:
Anyway, I created a simple but more comprehensive example @ stackblitz. Click 'RUN PROJECT' to see a demo. Click 'editor' (bottom right of the embedded screen) to view the code.
Feel free to fork and tinker with it.
Top comments (0)