3

I was refactoring my code and decided to replace ES6-classes with this-independent factory functions. So before the changes I had something like this:

class Person {
    constructor(name) {
        this.name = name;
    }
    get myName() {
        return `My name if ${this.name}`;
    }
    say() {
        console.log(this.myName);
    }
}

const john = new Person('John');
john.say();

Then it was changed to:

 function Person(name) {
    const obj = {
        name,
        get myName() {
            return `My name if ${obj.name}`;
        },
        say() {
            console.log(obj.myName);
        },
    };
    return obj;
}

const john = Person('John');
john.say();

It worked just the same, but when I used it in my Vue app, dealing with thousands of such objects, its performance dropped dramatically. I tried to split data, getters and methods into different objects and then to return their combination { ...data, ...getters, ...methods }, but nothing changed — I saw that classes were much more performant.

After some googling I found the possibility to add getters and methods via defineProperty:

function Person(name) {
    const obj = {
        name,
    };

    Object.defineProperty(obj, 'myName', {
        get() {
            return `My name if ${obj.name}`;
        }
    });
    Object.defineProperty(obj, 'say', {
        value() {
            console.log(obj.myName);
        }
    })

    return obj;
}

const john = Person('John');
john.say();

This solution worked, the application regained its performance, but I don't understand why. The only difference is probably that now the added properties aren't enumerable. But how could that make a problem?

So I would be very grateful for an explanation and probably a better solution.

Eugene Barsky
  • 5,780
  • 3
  • 17
  • 40
  • Factory functions are returning a new object every time containing all the properties as the own properties, whereas with a class, all the methods are inherited to the instances from the class. It's not clear, why the class should not be used. – Teemu Nov 29 '20 at 09:49
  • @Teemu The question wasn't about whether a class or a factory should be used here, and of course the methods of a class are put on the prototype for all the instanses. The question was why one variant of factory is more performant than the other. – Eugene Barsky Nov 29 '20 at 09:52
  • 1
    @EugeneBarsky : When using `defineProperty` the new property has `false` value in `writable`, `enumerable` and `configurable`. Whereas all these are `true` with assignment (1st version). – Easwar Nov 29 '20 at 10:14
  • 2
    I can't imagine any performance (speed-wise) difference between the given last two snippets in the context of the question. If the objects are heavily iterated in the app, then the lattest snippet acts more like a class, as the iterators are iterating the enumerable own properties only (excluding `for..in` and `getOwnPropertyDescriptors`). – Teemu Nov 29 '20 at 10:15
  • @Teemu That's why I was so surprized. The objects are indeed heavily iterated, but that cannot account for 20x difference in performance. With the first variant the app became virtually unusable. – Eugene Barsky Nov 29 '20 at 10:31
  • 1
    Why not? The complexity of the iteration is O(n), if you've an object with 20 properties, it takes 20x more time to iterate through than iterate an object with a single property only. Even with the examples, the 2nd snippet takes 3x time to iterate compared to other examples, then you'll multiple the difference with thousands, and the difference will grow from negilible to markable. – Teemu Nov 29 '20 at 10:37
  • 2
    How exactly are you using those thousands of `Person` instances in your Vue.js app? We really can't help you analyze the performance losses without seeing that code. – Bergi Nov 29 '20 at 10:44
  • @Bergi The `Person` example was made here to explain the problem. In reality thery are volumes of texts divided into lines (Line objects), and the Vue app searches through these lines and displays the relevant ones. – Eugene Barsky Nov 29 '20 at 11:44
  • Sounds like you'd need a simple data object instead of multipropertied innstances, perhaps even an array. Also, dynamic `this` is a very powerful core feature in JavaScript OOP, there's no reason to avoid it, unless you're goaling to functional programming. Not using `this` in OOP is like you'd have a 300 hp sports car, but you'd drive it with the starter only. – Teemu Nov 30 '20 at 07:33

0 Answers0