Search code examples
javascriptclassprototypejs

Javascript: How to correctly create a new custom istance from object definition (like a class)


I see that is still not possible to use class freely thanks to IE11.

So I would like to use prototypes and object.create in order to create my istances in the most lightwight way possible. However I only found to go with Object.assign() which is a sort of merger of object not a inizializator.

var classDefiner = {
   x: 1,
   y: 2,
   standardFunction : function() {
       return this.x+this.y;
   }
};

var istance = Object.assign({}, classDefiner ,{
    x: 10,
    z: 3,
    customFunctionA : function() {
        return this.x+this.y+this.z;
    },
    customFunctionB : function() {
        return this.x/this.y;
    }
});

Honestly this sound to me just a redundant copy of all methods of classDefiner in {}. Not a great use of prototypes or class, or object.create(). Is this the right way or is there something I can improve?

PS: The Challenge is to do not declare any single function/method but use instead a {} wrapper for the class and another for the istance for the sake of readability. I know I can add many method manually by using istance.newMethod or even newobject.prototype.newMethod. But this is long and messy if you have tons of method to add.


Solution

  • If you want to do it by copying the properties, what you're doing with Object.assign is probably as clean as it's going to get for now. (Note that you'll have to polyfill Object.assign on IE11, but it's fully polyfillable.) One tweak is you can use method syntax rather than property initializer syntax for the functions:

    var instance = Object.assign({}, classDefiner ,{
        x: 10,
        z: 3,
        customFunctionA() {                 // ***
            return this.x+this.y+this.z;
        },
        customFunctionB() {                 // ***
            return this.x/this.y;
        }
    });
    

    ES2018 will give us syntax that would replace Object.assign in the above:

    var instance = {
        ...classDefiner,                    // ***
        x: 10,
        z: 3,
        customFunctionA() {
            return this.x+this.y+this.z;
        },
        customFunctionB() {
            return this.x/this.y;
        }
    };
    

    (Of course that won't work on IE11 either without transpiling. :-) ) Technically the proposal for it is only Stage 3 at the moment, but it'll probably be Stage 4 in a couple of weeks after the January TC39 meeting, and is already implemented in V8 (in Chrome) and SpiderMonkey (in Firefox).

    If you want to do it with inheritance, you were on the right track with Object.create:

    var classDefiner = {
       x: 1,
       y: 2,
       standardFunction() {
           return this.x+this.y;
       }
    };
    
    var instance = Object.assign(Object.create(classDefiner), {
        x: 10,
        z: 3,
        customFunctionA() {
            return this.x+this.y+this.z;
        },
        customFunctionB() {
            return this.x/this.y;
        }
    });
    
    // Demonstrating it
    console.log(instance.standardFunction());
    console.log(instance.customFunctionA());

    In terms of whether to just copy the properties or inherit, I'd say there's not a lot in it either way.

    In favor of inheritance:

    • If, after creating instance, you added or modified classDefiner, those changes would be reflected in instance (because it inherits rather than copying).
    • There might technically be slightly less memory use inheriting instead of copying the properties, but unless you're doing millions of these, it's not something you're going to care about.

    In favor of copying:

    • Esp. as of the property spread notation mentioned above, the syntax is more concise. You can get close with a wrapper function, though.
    • In theory, there could be the tiniest additional overhead using inherited properties vs. copied "own" properties, but I doubt anything you'd need to care about on modern JavaScript engines.