tags:

views:

790

answers:

7

I've been using javascript for a while, but have never learned the language past the basics. I am reading John Resig's "Pro Javascript Techniques" - I'm coming up with some questions, but I'm not finding the answers to them in the book or on google, etc.

John gives this example in his book:
Function #1

function User( name, age ){
  this.name = name;
  this.age = age;
}
// Add a new function to the object prototype
User.prototype.getName = function(){
  return this.name;
};
User.prototype.getAge = function(){
  return this.age;
};
var user = new User( "Bob", 44 );
console.log("User: " + user.getName() + ", Age: " + user.getAge());

I'm still learning about the prototype property, so I tried writing something similar:
Function #2

function User (name, age ) {
  this.name = name;
  this.age = age;
  this.getName = function() {
    return this.name;
  };
  this.getAge = function() {
    return this.age;
  };
}
var user = new User( "Bob", 44 );
console.log("User: " + user.getName() + ", Age: " + user.getAge());

It doesn't use the prototype property to create the getName and getAge functions, but the output is the same as John's example.

I took it one step further, and created this:
Function #3

var User = {
  name: "",
  age: 0,
  setName: function(name) {
    this.name = name;
  },
  setAge: function(age) {
    this.age = age;
  },
  getName: function() {
    return this.name;
  },
  getAge: function() {
    return this.age;
  }
};
User.setName("Bob");
User.setAge(44);
console.log("User: " + User.getName() + ", Age: " + User.getAge());

Again - it looks different than John's example (and I had to add setter methods), but the output is the same.

Question #1 - what is the difference between the 3 functions? What is the advantage of the prototype property, and is Function #2 doing anything incorrectly, because it seems more straight forward to code #2 instead of #1 (although I'm sure #1 is doing it better seeing as John created it).

Question #2 - How could I modify function #3 to not use the setName and setAge methods, but still keep the {...} shorthand? Can the {...} shorthand have constructors?

Thanks in advance for helping me learn!

EDIT I think my 2nd question was a little confusing. I meant how could I use the {...} shorthand to create a User object, but then after I create the object, say something like:

var user = new User("Bob", 44);

Just like in Function #1 - or is that not possible?

EDIT #2 Wow! Thanks everyone for the awesome answers. That really makes it a lot more clear to me. So if I understand correctly, the difference between #1 and #2 aren't too much. If I only ever create one "User" object - they probably aren't different at all. But if my program creates many User objects, #1 would most likely be more efficient and use less memory since all objects will share the same functions.

I really appreciate all of the great answers - Thanks!

+10  A: 

Every time a function() {} is evaluated, it creates a new function object. Therefore, in #1 all of the User objects are sharing the same getName and getAge functions, but in #2 and #3, each object has its own copy of getName and getAge. All of the different getName functions all behave exactly the same, so you can't see any difference in the output.

The {...} shorthand is a constructor. When evaluated, it constructs a new "Object" with the given properties. When you run "new User(...)", it constructs a new "User". You happen to have created an Object with the same behavior as a User, but they are of different types.

Response to comment:

You can't, directly. You could make a function that creates a new object as per #3. For example:

function make_user(name, age) {
    return {
        name: name,
        age: age,
        getName: function() { return name; },
        getAge: function() { return age; },
    };
}

var user = make_user("Joe", "18");
Glomek
I guess I mean question #2 is how can I modify function #3 so that I can say something like var person = new User();
BrianH
This comment is not quite true 'All of the different getName functions all behave exactly the same'. Type #2 have access to private vars
meouw
My point was they behave the same within the example.
Glomek
Does type #1 not have access to private vars? Or are you saying the getName function in #2 has access to the age, but the getName function in #1 only has access to the name and not the age?
BrianH
There is a way of using lexical scope to fake private variables. You probably shouldn't worry about this for now, but if you're curious, see http://www.crockford.com/javascript/private.html
Glomek
That is a little deep, but I saved it to my delicious account for a later time - thanks!
BrianH
+3  A: 

2:

You can access name and age, without using such functions. In javascript you have to use different hacks to keep something private or protected.

This

User.name = "BoB";
User.age = 44;

will produce same output as your example.

There are no constructors as they appear in other languages. Easiest way would be to just define init() function and call it right after you instance the object.

But my biggest tip for you is to look into http://www.prototypejs.org/. It's a javascript library with a lot of cool features which tries to make javascript "more OO*".

Using prototype library you can make classes to behave more like real OOP classes. It also features constructors.

Edit: As for what you asked in your comment:

person = new User();
person.name = "Bob";
person.age = 44;
Maiku Mori
A: 

Question #1

prototype has the benefit of monkey patching. As the first example shows, the function are added after-the-fact. You can continue this to add or replace any methods you need (though, with fair warning).

Defining objects like #2 is more along the lines of classic OOP. But, then again, monkey patching isn't allowed in all OOP languages.

Question #2

In your 3rd function, you don't even need the get and set functions -- name and age are public properties (the potential downside to {}).

var User = {
  name: "",
  age: 0
};

User.name = 'Bob';
User.age = 44;

console.log("User: " + User.name + ", Age: " + User.age);

When you create an object using {} (an object literal), {} is the constructor (varying on browser). But, essentially, no you can't use a constructor in this format.

Jonathan Lonowski
Adding a property or method to a user defined object doesn't qualify as monkey patching. Monkey patching is adding methods or properties to the built in objects i.e. changing the behavior of the language itself.
meouw
+2  A: 

Your example #1 shows the usage of the prototype property. This property is available to all javascript objects you create and allows you to add properties or functions to the object declaration, so you had a object with 2 properties and later on you added 4 functions (getters and setters).

You should see the prototype property as the way to modify your object specification at runtime, say you have an object called name:

var Name = {
  First: "",
  Last: ""
};

You can use the prototype to add a function getFullName() later on by simply:

Name.prototype.getFullName = function() { return this.First + " " + this.Last; }

In the example 2 you inline the declaration of these getters and setters in the object declaration so in the end they are the same. Finally on the 3rd example you use the JavaScript object notation you should see JSON.

About your question 2 you can just declare your object as:

var User = {
  name: "",
  age: 0
};

this will give you the same object without getters and setters.

Paulo Lopes
+6  A: 

If you want to do OOP in JavaScript I'd highly suggest looking up closures. I began my learning on the subject with these three web pages:

http://www.dustindiaz.com/javascript-private-public-privileged/

http://www.dustindiaz.com/namespace-your-javascript/

http://blog.morrisjohns.com/javascript_closures_for_dummies

The differences between 1, 2, and 3 are as follows: 1) Is an example of adding new methods to an existing object. 2) Is the same as #1 except some methods are included in the object in the User function. 3) Is an example of defining an object using JSON. The shortcoming is that you cannot use new (at least not with that example) to define new instances of that object. However you do get the benefit of the convenient JSON coding style.

You should definitely read up on JSON if you don't know it yet. JavaScript will make a lot more sense when you understand JSON.

edit If you want to use new in function #3 you can write it as

function User() {
  return {
    name: "",
    age: 0,
    setName: function(name) {
      this.name = name;
    },
    setAge: function(age) {
      this.age = age;
    },
    getName: function() {
      return this.name;
    },
    getAge: function() {
      return this.age;
    }
  };
}

Of course all of those functions and properties would then be public. To make them private you need to use closures. For example you could make age and name private with this syntax.

function User() {
  var age=0;
  var name="";
  return {
    setName: function(name_) {
      name = name_;
    },
    setAge: function(age_) {
      age = age_;
    },
    getName: function() {
      return name;
    },
    getAge: function() {
      return age;
    }
  };
}
Bernard
Thanks for that... hope you're still around and answering stuff on SO :)
Yar
A: 

You seem to have some good answers here but you might like to look at this question: best-approach-to-member-variables-in-object-oriented-javascript. This was my answer which describes the differences and similaries.

meouw
A: 

If you interested on JSON style JavaScript class declaration talks...

http://mahtonu.wordpress.com/2010/04/13/json-style-javascript-object-declaration/

M A Hossain Tonu