39

Possible Duplicate:
Use of ‘prototype’ vs. ‘this’ in Javascript?
Object Oriented questions in Javascript

Can you please recommend which of the following is the best or their pros and cons?

Method 1

function User() {

    this.name = "my name";
    this.save = function() {

    };
}

Method 2

function User() {

    this.name = "my name";
}

User.prototype.save = function() {

}
informatik01
  • 16,038
  • 10
  • 74
  • 104
Arunoda Susiripala
  • 2,516
  • 1
  • 25
  • 30
  • Method 2 is better in most situations, but it might depend on exactly what code is in your save method (e.g., in method 1 `save()` would have access to any local variables created inside `User()`). – nnnnnn Nov 02 '12 at 05:59
  • @nnnnnn But then why is method 2 better? I would think an object's method most likely needs access to its properties, so why isn't method 1 better? – Ian Nov 02 '12 at 06:04
  • 2
    Variables and properties are not the same thing. In Method 2 the `.save()` method _does_ have access to the object's properties. Either way the `.save()` method can access `this.name`. But in Method 2 `.save()` would not have access to local variables in the constructor function - of which there are none in your example, but a more complicated "class" might or might not need to do. – nnnnnn Nov 02 '12 at 06:05
  • I came across an article that talks about that and more http://www.phpied.com/3-ways-to-define-a-javascript-class/ – AurA Nov 02 '12 at 06:05
  • I've written Javascript code in Visual Studio 2010 (which has minimal support for JS). I usually created 'classes' using first method it gave me some member variable suggestions when I used the class later on. It also resulted in fewer run-time errors compared to method 2, which were largely due to human error when copy-pasting code and forgetting to update ".prototype." declarations... – Mike Trusov Nov 02 '12 at 06:06
  • @nnnnnn Sorry, I thought you meant the same thing, so that's why I was confused. So you mean like if you declared `var testing = "asdf";`, then method 2 obviously wouldn't have access to it. But if you had `this.testing = "asdf";`, both methods would have access to `this.testing`, right? – Ian Nov 02 '12 at 06:07
  • @Ian - Yes, that's right. Peter Wilkinson's answer gives an example of what I meant too. – nnnnnn Nov 02 '12 at 06:10
  • @nnnnnn yes. But it will expose variable `testing` to outside. – Arunoda Susiripala Nov 02 '12 at 06:31
  • 3
    I really prefer Method 1 because it sort of encapsulates all the "class" declarations inside a single definition. BTW, a really good article on OOP http://killdream.github.com/blog/2011/10/understanding-javascript-oop/ – Eldelshell Nov 02 '12 at 12:13
  • This is what I use http://lavinski.tumblr.com/post/6845551282/javascript-module-pattern – Daniel Little Nov 03 '12 at 08:08
  • ECMA 2015 now supports the [keyword `class` and `constructor`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes) – Liam Jul 31 '19 at 11:19

4 Answers4

57

Coming from a background in Java and PHP, I had initially struggled with the whole 'class' concept in JavaScript. Here are a few things to think about.

Construction

First, since you're likely going to be defining your classes as..

Customer = function () {}
Customer.prototype = new Person();

You will end up running into all sorts of nightmares of code if you are defining properties and methods during construction. The reason being is that a piece of code like Customer.prototype = new Person(); requires that the Person be called in the Customer constructor for true inheritance.. otherwise you'll end up having to know what the original one sets at all times. Take the following example:

Person = function (name) {
    this.name = name;
    this.getName = function () {return this.name}
}
Customer = function (name) {
    this.name = name;
}
Customer.prototype = new Person();

Now we're going to update Person to also set whether they are 'new':

Person = function (name, isNew) {
    this.name = name;
    this.isNew = isNew;
    this.getName = function () {return (this.isNew ? "New " : "") + this.name; }
}

Now on any 'class' that is inheriting from the Person, you must update the constructor to follow form. You can get around that by doing something like:

Customer = function () {
    Person.apply(this, arguments);
}

That will call 'Person' in the scope of the new 'Customer', allowing you to not have to know about the Person construction.

Speed

Take a look at these benchmarks: http://jsperf.com/inherited-vs-assigned.
Basically, what I am attempting to prove here is that if you're creating these objects en masse, your best route is to create them on the prototype. Creating them like:

Person = function (name) {
    this.name = name;
    this.getName = function () {return this.name}
}

is very slow, because for every object creation, it creates a new function - it doesn't simply look up the prototype chain for the already existing one. Conversely, if you've got only a few objects that the methods are called extremely frequently on, defining them locally helps with the speed lost by looking up the prototype chain.

Shared properties

This always gets me. Let's say you've got something like the following:

Person = function () {
    this.jobs = {};
    this.setJob = function (jobTitle, active) {this.jobs[jobTitle] = active; }
}

Employee = function () {}
Employee.prototype = new Person();

var bob = new Employee();
bob.setJob('janitor', true);

var jane = new Employee();
console.log(jane.jobs);

Guess what? Jane's a janitor! No joke! Here's why. Since you didn't define this.jobs as being a new object on instantiation of the Employee, it's now just looking up the prototype chain until it finds 'jobs' and is using it as is. Fun, right?

So, this is useful if you want to keep track of instances, but for the most part you're going to find it incredibly frustrating. You can get around that by doing the following:

Employee = function () { Person.apply(this); }

This forces 'Person' to create a new 'this.jobs'.

Private variables

Since there's nothing that's really "private" in JavaScript, the only way you can get private variables is to create them in the constructor, and then make anything that relies on them initialize in the constructor.

Person = function () {
    var jobs = {};
    this.setJob = function (jobTitle, active) {jobs[jobTitle] = active; }
    this.getJob = function (jobTitle) { return jobs[jobTitle]; }
}

However, this also means that you must call that constructor on every instantiation of an inherited class.


Suggestion

http://ejohn.org/blog/simple-javascript-inheritance/

This is a super basic class setup. It's easy. It works. And it'll do what you need it to do 90% of the time without having to deal with all the insanity JavaScript has to offer :)

</rant>

informatik01
  • 16,038
  • 10
  • 74
  • 104
Stephen
  • 5,362
  • 1
  • 22
  • 33
10

Best is a very subjective term.

Method 1 gives the possibility of truly private variables. e.g :

function User() {
   var name = "fred";
   this.getName = function () { return name;};
}

Method 2 will use less memory, as a single save function is shared by all User objects.

'Best' will be determined by your specific requirements.

Programming Guy
  • 7,259
  • 11
  • 50
  • 59
7

JavaScript is an "Object Based" language, not a "Class Based" language. Shared behaviours, which is the class concept, are implemented by linking prototypes.

Method 1 does NOT create a class. Every time you invoke it, you create attributes and functions as defined and they are NOT shared with other "instances". If you replace this.save then only the one instance will have altered behaviour.

Method 2 implements shared behaviours. Therefore it is what people associate with classes and instances. If you replace this.save withe a different function, then all derived instances will immediately have the new behaviour.

If "best" means no memory consuming redefinitions of functions and sharing common behaviours (which is what class based programming tends to equate to) the Method 2 is the way to go.

CyberFonic
  • 3,957
  • 1
  • 21
  • 21
6

As others have mentioned, there are two main differences:

  • Method 1 will create a new function for every instance of User
  • Method 1 will be able to access local variables in the constructor's scope

Here's an example to demonstrate these:

function User() {

    var name = "my name";

    this.foo = function() {
        // one function per User instance
        // can access 'name' variable
    };
}

User.prototype.bar = function() {
    // one function shared by all User instances
    // cannot access 'name' variable
};

var a = new User();
var b = new User();

console.log(a.foo === b.foo); // false; each instance has its own foo()
console.log(a.bar === b.bar); // true; both instances use the same bar()
grc
  • 22,885
  • 5
  • 42
  • 63