3

Is it possible to create JavaScript class without using 'this' keyword? Expressions like

var self = this;
...
self.property = value;

is also prohibited. I dont want to think about context and use only vars.

Take a look at this simple class:

function Timer() {
  var interval = 0;
  var count = ++Timer.totalCount;

  function setInterval(val) {
    interval = val;
  }
  function run() {
    setTimeout(done, interval);
  }
  function done() {
    var div = document.createElement('div');
    div.innerHTML = 'Timer #' + count + ' finished in ' + interval + ' ms';
    document.body.appendChild(div);
  }

  return {
    setInterval: setInterval,
    run: run
  }
}
Timer.totalCount = 0;

var t1 = new Timer();
var t2 = new Timer();
var t3 = new Timer();

//how to directly set interval value without set function?
t1.setInterval(3000);
t2.setInterval(2000);
t3.setInterval(1000);

t1.run();
t2.run();
t3.run();

This class does not use 'this' at all.

Is it correct?

Can I create real classes like that? Is there potential problems?

How to create property in this class without get/set function?

Is that class possible to extend using inheritance?

Alexey Obukhov
  • 834
  • 9
  • 18
  • 2
    The big question I have is... why? – James Donnelly Apr 13 '17 at 11:08
  • So whats wrong with your code? – ands Apr 13 '17 at 11:15
  • 1
    The OP's code does not implement a constructor but a factory. Invoking a function like the above (returning an object with references into the function) creates a closure. Therefore the OP now is dealing with scope instead of context. And thus invoking such a function with `new` might be a hint that the OP does not yet fully understand the mechanism behind a real instantiation. The biggest disadvantage of this pattern is its memory consumption in case one does use it for the creation of big type systems with a vast amount of created objects which is due to sacrificing prototypal delegation. – Peter Seliger Dec 03 '22 at 22:41

2 Answers2

3

First of all, this is a basic thing in JavaScript. It's very recommended that you can understand it and reason about its context.

If you want a more "traditional" OOP code and are able to use ES6/ES2015 (or transpiling with Babel), all the reasoning about this also becomes easier and all the code more clean. Here's your sample rewritten with ES2015, including the inheritance part:

class Timer {
  constructor(count, interval) {
    this.interval = interval
    this.count = count
  }

  run() {
    setTimeout(() => {
      const div = document.createElement('div')
      div.innerHTML = `Timer #${this.count}
        finished in ${this.interval} ms`
      document.body.appendChild(div)
    }, this.interval)
  }
}

class OneSecondTimer extends Timer {
  constructor(count) {
    super(count, 1000)
  }
}

const t1 = new Timer(1, 3000)
const t2 = new Timer(2, 2000)
const t3 = new OneSecondTimer(3)

t1.run()
t2.run()
t3.run()
Erick Petrucelli
  • 14,386
  • 8
  • 64
  • 84
1

This is similar to the class-free style advocated by Douglas Crockford in chapter 17 of his book How JavaScript Works, though a few improvements should be made to match that style even better:

  • Use let and const instead of var.
  • Do not call your function with new, it's pointless if the function has a return statement.
  • Do not emulate static members as properties on the function. Instead, use an immediately-invoked function expression to hide them in a closure.
  • Use Object.freeze on the returned function container so it cannot be accidentally modified.

const createTimer = function iife() {
    let counter = 0;

    return function createTimer() {
        const id = ++counter;
        let interval = 0;

        function setInterval(val) {
            interval = val;
        }
        function done() {
            const div = document.createElement('div');
            div.innerHTML = `Timer #${id} finished in ${interval}ms`;
            document.body.appendChild(div);
        }
        function run() {
            setTimeout(done, interval);
        }
        
        return Object.freeze({ setInterval, run });
    }
}();

const t1 = createTimer();
const t2 = createTimer();
const t3 = createTimer();

t1.setInterval(3000);
t2.setInterval(2000);
t3.setInterval(1000);

t1.run();
t2.run();
t3.run();

This style of programming doesn't support inheritance because it doesn't even have classes or custom prototypes. Instead, it's a style that favors composition and strict encapsulation of mutable state.

The above timer constructor is, however, a poor example, since the timer has a side-effect on the document. A more reusable timer object would probably provide a promise that resolves when the time has passed.

Domino
  • 6,314
  • 1
  • 32
  • 58