0

reading the What techniques can be used to define a class in JavaScript, and what are their trade-offs? on stackoverflow i understand that i can define a class via

Method 1:

function Person(name, gender){
   this.name = name;
   this.gender = gender;
}

and add functions in prototype so as to avoid member functions recreated every time its instantiated. like

Person.prototype.speak = function(){
   alert("my name is" + this.name);
}

and create its instances via

var person = new Person("Bob", "M"); 

I think the creation of same object is possible with out new keyword like

Method 2:

var Person = function (name, gender) {

    return {name:name, gender:gender}; 

}

person  = Person("Bob", "M");

Is the second method doing exactly the same thing done by the first one? Also if so how would i mock up addition of functions via prototype (as we see in method 1's speak) in the second approach?

Community
  • 1
  • 1
Mithun Satheesh
  • 27,240
  • 14
  • 77
  • 101

4 Answers4

5

No, Method 2 != Method 1. The second method is creating a new anonymous object with a prototype that points at Object.prototype while the first is creating a new object with a prototype that points at Person.prototype.

In psuedo-code:

// Method #1
function Person(name, gender) {
    // The magic JS does *for us* (but *only* when invoked with `new`)
    var this = {};
    // __proto__ is the *internal* prototype reference
    // It's not required to be accessible unless you're in an ES6 environment.
    this.__proto__ = Person.prototype;

    // Person.prototype is also created by JS for us
    // and provided with a reference (among other things)
    // to this function as prototype.constructor.

    // Our stuff
    this.name = name;
    this.gender = gender;

    // More JS magic
    return this;
}

// Method #2
function Person(name, gender) {
    // Just our stuff - no magic JS goodness here
    return {
        name: name, gender: gender
    };
}
Sean Vieira
  • 155,703
  • 32
  • 311
  • 293
  • 2
    JS doesn't do `var this = {}; this.prototype = Person.prototype; this.constructor = Person;`. That's totally wrong. Instead it does `var this = Object.create(Person.prototype);`. I'll remove the downvote when you correct your mistake. Also the prototype of the second object (the literal object) points to `Object.prototype`, and not `Object`. – Aadit M Shah Jul 03 '13 at 02:51
  • @AaditMShah - good points, both :-) I've updated my answer to correct the mistake and to make it clear that it's the *internal* prototype property that's being set (not a necessarily accessible property named `prototype`). Thanks! – Sean Vieira Jul 03 '13 at 12:19
  • 1
    @SeanVieira No problem. I removed the downvote. BTW the `constructor` property is not set on the instance. It's inherited from the prototype. – Aadit M Shah Jul 03 '13 at 13:12
3

As Sean Vieira explained the first method is not the same as the second method. In the first method the instance inherits from Person.prototype. In the second method the instance inherits directly from Object.prototype.

Method 1:

        null
         ^
         |
         | __proto__
         |
+------------------+
| Object.prototype |
+------------------+
         ^
         |
         | __proto__
         |
+------------------+
| Person.prototype |
+------------------+
         ^
         |
         | __proto__
         |
+------------------+
|      person      |
+------------------+

Method 2:

        null
         ^
         |
         | __proto__
         |
+------------------+
| Object.prototype |
+------------------+
         ^
         |
         | __proto__
         |
+------------------+
|      person      |
+------------------+

Interestingly the above diagrams show you that objects inherit from other objects in JavaScript, and not from constructors. Hence when you create new Person the instance inherits from Person.prototype, not from Person itself.

How is this relevant information? For starters it demonstrates that you don't need to create a constructor to creates instances of an object. Instead you directly create the prototype object as follows:

var person = {
    create: function (name, gender) {
        var person = Object.create(this);
        person.gender = gender;
        person.name = name;
        return person;
    },
    speak: function () {
        alert("My name is " + this.name + ".");
    }
};

In the above example person is equivalent to Person.prototype. You may now create an instance of person as follows:

var bob = person.create("Bob", "M");

The prototype chain of bob will look like this:

        null
         ^
         |
         | __proto__
         |
+------------------+
| Object.prototype |
+------------------+
         ^
         |
         | __proto__
         |
+------------------+
|      person      |
+------------------+
         ^
         |
         | __proto__
         |
+------------------+
|       bob        |
+------------------+

So why should you create objects like this instead?

  1. It looks cleaner. Everything is encapsulated in a single object literal.
  2. It's easier to understand that objects inherit from objects. No constructors needed.
  3. You don't need to use new to create an instance. This solves a lot of problems.

For more information about this pattern read my blog post on "Why Prototypal Inheritance Matters".

Community
  • 1
  • 1
Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
  • Interesting blog, I'm curious if you could explain the 3 reasons why prototypal patterns are better than constructor functions: 1: Is technically possible but you don't like the syntax (cleared up in comments). 2. Same as 1, don't like the syntax and prone to make mistakes. 3. Don't like the syntax, too confusing (same as 1 and 2). What I am missing is technical foundation as being better extendable or will perform better. To some point I agree that constructor functions could be error prone but even before using closure compiler I hardly ever misuse them (forgetting new) – HMR Jul 03 '13 at 05:21
  • @HMR I already answered the advantages of prototypal inheritance over classical in the following answer: http://stackoverflow.com/a/16872315/783743. Nevertheless I must admit that both these patterns are equivalent in power and choosing between one of them is just a matter of preference. However you should note that `new` is much faster than `Object.create`. Hence if you want performance then stick with `new`. I've created a small, fast library which makes working with constructors and prototypes much easier (especially for those who don't understand it): https://github.com/javascript/augment – Aadit M Shah Jul 03 '13 at 06:03
  • Thank you for your reply, maybe this is an interesting read for you. Michael Bolin has worked on the closure compiler and defends the constructor function over functional pattern It's an old one and doesn't mention Object.create (prototypal pattern?) http://bolinfest.com/javascript/inheritance.php At some point JavaScript will have classes (Ecma 6) – HMR Jul 03 '13 at 06:49
  • +1 for prototypal but I'm not sure it's the same as the OP's method 2 as that looks like the functional pattern. – HMR Jul 03 '13 at 07:15
  • @HMR No. Crockford's functional pattern is definitely not the same as my [prototypal pattern](http://aaditmshah.github.io/why-prototypal-inheritance-matters/#constructors_vs_prototypes). The difference is that the functional pattern doesn't have inheritance in the form of [delegation](http://aaditmshah.github.io/why-prototypal-inheritance-matters/#delegation_or_differential_inheritance). Instead it extends a single object with new properties. What Crockford does is similar to what this answer does: http://stackoverflow.com/a/17008693/783743 It satisfies Liskov's substitution principle. Google. – Aadit M Shah Jul 03 '13 at 15:57
0

Your examples are achieving the same thing with regards to accessing the properties, but they are not identical as the first method has the basic Object prototype. The answer to your second question is:

function Person( name, gender ){
   return {
     name: name,
     gender: gender,
     speak: function(){
        alert("my name is" + this.name);
     }
   };
}

Here there is no prototype. The function is baked into the object literal. The only value in using a literal instead of a prototype would be to create a closure so you can have private variables e.g:

function Person( name, gender ){
   return {
     speak: function(){
        alert("my name is" + name);
     }
   };
}
Tim
  • 8,036
  • 2
  • 36
  • 52
0

Maybe this answer can explain a bit more about constructor functions, inheritance, private variables and overriding methods: Prototypical inheritance - writing up

The other answers already address your question; creating an object as an object literal {prop:val,method:function(){}} and creating an object using constructor functions: var MyObject=function(){this.prop=val};MyObject.prototype.method=function(){};

Community
  • 1
  • 1
HMR
  • 37,593
  • 24
  • 91
  • 160