313

I am wondering if there are any advantages of using any of these over the other, and which way should I go?

Constructor approach:

var Class = function () {

    this.calc = function (a, b) {
        return a + b;
    };

};

Prototype approach:

var Class = function () {};

Class.prototype.calc = function (a, b) {
    return a + b;
};

I don't like that, using the prototype, method definitions are separated from the class, and I'm not aware if there is any specific reason I should use this over just the first approach.

Also, is there any benefit of using a function literal to define a "class", over just function definition:

var Class = function () {};

vs

function Class () {};

Thanks!

Leo
  • 3,617
  • 3
  • 18
  • 26

3 Answers3

468

Methods that inherit via the prototype chain can be changed universally for all instances, for example:

function Class () {}
Class.prototype.calc = function (a, b) {
    return a + b;
}

// Create 2 instances:
var ins1 = new Class(),
    ins2 = new Class();

// Test the calc method:
console.log(ins1.calc(1,1), ins2.calc(1,1));
// -> 2, 2

// Change the prototype method
Class.prototype.calc = function () {
    var args = Array.prototype.slice.apply(arguments),
        res = 0, c;

    while (c = args.shift())
        res += c;

    return res; 
}

// Test the calc method:
console.log(ins1.calc(1,1,1), ins2.calc(1,1,1));
// -> 3, 3

Notice how changing the method applied to both instances? This is because ins1 and ins2 share the same calc() function. In order to do this with public methods created during construction, you'd have to assign the new method to each instance that has been created, which is an awkward task. This is because ins1 and ins2 would have their own, individually created calc() functions.

Another side effect of creating methods inside the constructor is poorer performance. Each method has to be created every time the constructor function runs. Methods on the prototype chain are created once and then "inherited" by each instance. On the flip side of the coin, public methods have access to "private" variables, which isn't possible with inherited methods.

As for your function Class() {} vs var Class = function () {} question, the former is "hoisted" to the top of the current scope before execution. For the latter, the variable declaration is hoisted, but not the assignment. For example:

// Error, fn is called before the function is assigned!
fn();
var fn = function () { alert("test!"); } 

// Works as expected: the fn2 declaration is hoisted above the call
fn2();
function fn2() { alert("test!"); }
Andy E
  • 311,406
  • 78
  • 462
  • 440
  • 36
    Aah, that makes things so much clearer :) I didn't realize the efficiency difference - that's very useful to know. Same for the hoisting effect - tricky, indeed. Thanks for such a great answer, I learned a lot from it! – Leo Dec 22 '10 at 11:09
  • You forgot to mention that it's harder to use Super.call.someFunction() when overriding in inheritance: http://stackoverflow.com/a/16063711/1641941 – HMR Sep 18 '13 at 00:55
  • 5
    Very old question, but somehow followed a link and stumbled here -- I think the example would be more telling if you kept the number of arguments consistent (just to demonstrate that it's using `a+b`. This really is a small point, but it helps the reader identify the diff that you're concentrating on as well as rule out other factors he may be reading (for instance: *what happens in the first call if you did have a third argument*). The example is simple enough and hopefully the programmer is good enough not to get caught up on the small differences. – vol7ron Apr 03 '14 at 14:43
  • 1
    Surely the same could be achieved using the Functional pattern and thus avoiding having to ape the inheritance patterns of classical languages. – timebandit Apr 15 '15 at 10:44
  • Isn't it more dangerous in the prototypical approach? I can accidentally change function assignment for all classes without knowing it. Whereas the Constructor approach enforces that if you want to change a function, you'd have to find the code and change it. – benjaminz Apr 22 '16 at 14:49
  • 1
    what is this part of the code doing: `Class.prototype.calc = function () { var args = Array.prototype.slice.apply(arguments), res = 0, c;` – Jwan622 Oct 31 '17 at 03:12
  • 1
    Since some time has passed and we now have ES6+, I'd like to note that arrow function definitions are shorthand for `var Class = function() {...}` and are therefore not hoisted either. – Jonathan Rys Apr 19 '18 at 19:39
  • 1
    @Jwan622, it converts `arguments` to an (real) array so we can use array functions such as `shift()`. Note that `Object.prototype.toString.call(arguments)` returns `[object Arguments]` while `Object.prototype.toString.call([])` returns `[object Array]`. – WynandB Sep 10 '18 at 23:52
70

The advantage of the prototype approach is efficiency. There is one calc() function object shared between all Class objects (by which I mean objects created by calling the Class constructor). The other way (assigning methods within the constructor) creates a new function object for every Class object, using more memory and taking more processing time when calling the Class constructor. However, this approach does have an advantage: the calc() method has access to local variables within the constructor, which you can use to your advantage:

function Class() {
    var calcCallCount = 0;

    this.calc = function (a, b) {
        ++calcCallCount;
        alert("Calc called " + calcCallCount + " times");
        return a + b;
    };
};

Regarding var Class = function() {...} versus function Class() {...}, I generally prefer the latter is because it means the function has a name, which can be useful when debugging. The other difference is that the latter version (a function declaration) is hoisted, meaning that it is available everywhere within the scope in which it is defined, not just after the definition. However, some people prefer to use the former (a function expression) everywhere.

Tim Down
  • 292,637
  • 67
  • 429
  • 506
  • Thanks for your answer too, Tim, I appreciate it! – Leo Dec 22 '10 at 11:20
  • Re `Class = function() {...}`, i.e. defining in global/window scope, I've not had any debugging problems with this approach in terms of name, although understandably hoisting does not seem to occur. Not sure if there were any other differences between this approach and your two. – Engineer Jul 27 '14 at 17:05
  • 2
    @NickWiggill: Built-in browser developer tools have come a long way since I wrote this answer and they now do a much better job of inferring an appropriate function name from the context, so I agree that ease of debugging is much less of a concern these days. – Tim Down Jul 27 '14 at 19:05
  • 3
    Made a js Perf test to visualize performance differences. http://jsperf.com/class-comparison – Bernhard Feb 10 '15 at 09:52
  • @Bernhard just to clarify, all of the performance hit is in the instantiation of the object. I made this jsperf to highlight that: http://jsperf.com/class-create-this-vs-proto . Interestingly, if you're just instantiating one instance of the class and using it it looks like it's faster to use `this.`. However, if you're going to be instantiating a ton of that class you definitely want to use `.prototype.`. Thanks for the jsperf! – Eric Barr Mar 11 '15 at 21:56
  • To avoid anonymous function in function expression, you can instead do "var Class = function namedFunc( ) {...}"; this way your function still have a name and is assigned to a variable. – Yiling Apr 07 '15 at 09:27
  • @YilingLu: Yes, that's a named function expression and is generally fine. However, there are [issues with it in old IE](https://kangax.github.io/nfe/), which was much more of a problem in 2010 than it is today. – Tim Down Apr 07 '15 at 09:44
  • It should be noted, that you can still have private methods while making use of the prototype, as seen in this post: https://stackoverflow.com/questions/1441212/javascript-instance-functions-versus-prototype-functions/1441692#1441692 – Dave Voyles Sep 27 '15 at 18:25
  • 1
    @DaveVoyles: Or indeed this post: https://stackoverflow.com/questions/9772307/declaring-javascript-object-method-in-constructor-function-vs-in-prototype/9772864#9772864 – Tim Down Sep 28 '15 at 08:32
48
var YourClass = function(){
  var privateField = "somevalue";
  this.publicField = "somevalue";
  this.instanceMethod1 = function(){
     //you may access both private and public field from here:
     //in order to access public field, you must use "this":
     alert(privateField + "; " + this.publicField);
  };
}

YourClass.prototype.instanceMethod2 = function(){
  //you may access only public field 2 from this method, but not private fields:
  alert(this.publicField);
  //error: drawaback of prototype methods:
  alert(privateField);  
};

Advantages of prototype methods:

  1. When you define methods via prototype, they are shared among all YourClass instances. As a result the total size of such instances is < than if you define methods in constructor; There are tests that show how method definition via prototype decrease the total size of html page and as a result a speed of its loading.

  2. another advantage of methods, defined via prototype - is when you use inherited classes, you may override such methods and in the overriden method of the derived class you may invoke the method of base class with the same name, but with methods defined in constructor, you cannot do this.

Jim Blackler
  • 21,883
  • 11
  • 82
  • 100
Alexandr
  • 8,399
  • 11
  • 54
  • 92
  • 2
    Thanks for your answer, I appreciate it and now realize even more how great a resource StackOverflow is. – Leo Dec 22 '10 at 11:20
  • 1
    Hello, what do you mean by inherited classes? I don't think it's the right terminology cuz javascript has no concept of classes.. When you said overridden method of the derived class, did you mean, another object, whose prototype is your object? I'm lost.. Can you please edit or explain? – Ren May 05 '16 at 06:15
  • Alexandr could you explain #2 with an example? – Govind Rai Jan 16 '17 at 20:04