DEV Community

Cover image for Class Free Object Oriented JS
KooiInc
KooiInc

Posted on • Edited on

Class Free Object Oriented JS

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)