Skip to content Skip to sidebar Skip to footer

What Are The Differences Between These Three Patterns Of "class" Definitions In Javascript?

Are there any important/subtle/significant differences under the hood when choosing to use one of these four patterns over the others? And, are there any differences between the t

Solution 1:

Just to be clear: JS doesn't know of classes, just objects and custom, self-defined constructor functions, but that's besides the point. To answer your question in short: yes, there are some small and even some fairly large differences between the various ways of creating a new object you're posting here.

CoffeeScript: This is actually the most clear-cut and traditional way to create your own constructor, but it has been "optimized" in the sense that it's been ready set-up to use (optional) closure variables. Basically, what this code does, is use an IIFE, to wrap both the constructor definition and the proptotype method assignments in their own, private scope, that returns a reference to the new constructor. It's just clean, simple JS, no different from what you might write yourself.

Knockout: Now this threw me a little, because to me, at least, the snippet you provide looks either like part of a module pattern, or a power constructor. But since you're not using strict mode, omitting the new would still make for dangerous situations, and since the entire function goes trough the trouble of creating a new instance of DifferentAnimal, only to then construct a second object literal, assigning all properties of DifferentAnimal to that secondary object, I'd say you're missing something. Because, truth be told, omitting the last return {}; statement here, would probably make no difference at all. Plus: as you can see, you're declaring a method (move) in what is, in essence, a constructor. This means that every instance will be assigned its own function object move, rather then getting it from the prototype. In short: have another close look at where you got this snippet from, and double-check if this is the full version, because if it is, I can only see arguments against this.

Using a variable, defined inside the constructor is simply: a closure, suppose your properties have a distinct initial state, determined by some arguments, passed to that constructor:

functionMyConstructor(param)
{
     var paramInit = param/2;//or somethingthis.p = paramInit;//this property can change later on, so:this.reInit = function()
     {//this method HAS to be inside constructor, every instance needs its own methodthis.p = paramInit;//var paramInit can't, it's local to this scope
     };
}
var foo = newMyConstructor(10);
console.log(foo.p);//5
foo.p = 'hi';
console.log(foo.p);//hi
foo.reInit();
console.log(foo.p);//5console.log(foo.paramInit);//undefined, not available outside object: it's a pseudo-private property

That's all there is too it, really. When you see ppl using var that = this; or something, that's often to create a reference to the main object that is available anywhere, without having to deal with the headaches of this (what does this reference? What should the method do when applied to an object other than the one it was originally intended for? etcetera...)

Backbone: Here, we're dealing with another case: extending objects (IE: using methods, properties of either an existing "class" (constructor) or a particular instance) is not the same as simply creating an object. As you well know, JS objects can be assigned new properties at any given time. Those properties can be removed, too. Sometimes, prototype properties can be redefined on the instance itself (masking the prototypal behaviour) etc... So it all depends on what you want the resulting object (the newly created object, that extends the given instance) to look like: do you want it to take all properties from the instance, or do you want both objects to use the same prototype somewhere down the line? Both of these things can be achieved by using simple JS, too, but they just take a bit more effort to write yourself. However, if you write, for example:

functionAnimal(name)
{
    this.name = name;
}
Animal.prototype.eat= function()
{
    console.log(this.name + ' is eating');
};

That could be deemed the equivalent of writing:

varAnimal = Object.extend({name:'',eat:function()
{
    console.log(this.name + ' is eating');
}});

A lot shorter, but lacking the constructor.

new vs Object.create Well, that's an easy one: Object.create just is a lot more powerful that new: you can define prototype methods, properties (including weather or not they are enumerable, writeable etc...) right at the time you need to create an object, instead of having to write a constructor and a prototype, or create an object literal and mess around with all those Object.defineProperty lines. The downsides: Some people still aren't using ECMA5 compliant browsers (IE8 is still not quite dead). In my experience: it does become quite hard to debug sizeable scripts after a while: though I tend to use power-constructors more than I do regular constructors, I still have them defined at the very top of my script, with distinct, clear and quite descriptive names, whereas object-literals are things I just create "on-the-fly". Using Object.create, I noticed I tend to create objects that are really a little too complex to qualify as actual object literals, as though they are object literals:

//fictional example, old:var createSomething = (function()
{
    var internalMethod = function()
    {//method for new objectconsole.log(this.myProperty || '');
    };
    returnfunction(basedOn)
    {
        var prop, returnVal= {};
        returnVal.myProperty = newDate();
        returnVal.getCreated = internalMethod;//<--shared by all instances, thx to closureif (!basedOn || !(basedOn instanceofObject))
        {//no argument, or argument is not an object:return returnVal;
        }
        for (prop in basedOn)
        {//extend instance, passed as argumentif (basedOn.hasOwnProperty(prop) && prop !== '_extends')
            {
                returnVal[prop] = basedOn[prop];
            }
        }
        returnVal._extends = basedOn;//<-- ref as sort-of-prototypereturn returnVal;
    };
}());

Now this is pretty verbose, but I've got my basic constructor ready, and I can use it to extend an existing instance, too. It might seem less verbose to simply write:

var createSomething = Object.create(someObject, {getCreated:function()
{
    console.log(this.myProperty);
},
myProperty:newDate()});

But IMO, this makes it harder on you do keep track of what object is created where (mainly because Object.create is an expression, and will not be hoisted.Ah well, that's far from a conclusive argument of course: both have their pro's and con's: I prefer using module patters, closures and power constructors, if you don't that's just fine.

Hope this cleared up a thing or 2 for you.

Solution 2:

The first example puts the move function in the prototype which will be shared between all Animal instances.

The second example creates a new move function for every the animal instance.

The third example generates a Animal class with the move function in the prototype similar to the first example but with allot less code. (In your example the name is also shared between all instances, which you probably don't want)

Putting the function in the prototype makes instantiating Animals faster, and because of the way JIT engines work even the execution of the function is faster.

Post a Comment for "What Are The Differences Between These Three Patterns Of "class" Definitions In Javascript?"