tags:

views:

213

answers:

6

Hi all, I wonder if someone could be kind enough to explain the function.prototype thingie (thingie!!??) in OO javascript.

I come from a server side programming background, and may be I am not grasping the whole concept of prototypes,

Given the following snippets of code:

var animate=function(){};
animate.angular=function(){/*does something here*/};
animate.circular=function(){/*does something here*/};

And

var animate=function(){};
animate.prototype.angular=function(){/*does something here*/};
animate.prototype.circular=function(){/*does something here*/};

as far as I can tell, both the latter functions are callable via animate.angular(/*args*/) and animate.circular(/*args*/) so, I guess my question is, what are the merits of defining the functions in the second way? and how or why are they different?

Hope I made sense...

EDIT: Thankyou all for the enlightening answers, It's very hard to judge an answer here as being "Correct", so I'm gonna mark the one I feel made the most contribution...

You all have certainly given me more food for thought...

+2  A: 

Javascript is a weird language ... very powerful but not all that strongly structured compared with other languages...

The prototype is how JavaScript lets you save memory if you're going to be creating multiple instances of a class... So if you're using JS in an OOP way you should define your functions as part of the prototype. Also, there are ways to simulate inheritance using the prototype.

I highly recommend the book "Javascript, the Good Parts" for lots of great explanation about this.

gunderson
There's also a Google Tech Talk of the same name: http://www.youtube.com/watch?v=hQVTIJBZookJust watched it today.
Jon Homan
@gunderson: "Weird" is a very subjective expression. After properly learning JS for 6 months, I find myself programming with it more fluently and more naturally than I ever have using Java or C++. I cannot, therefore, agree with you.
Tom Bartel
This is why I love mootools, it exposes javascript inheritance with a style I appreciate.
rpflo
Tom Bartel: Oh I didn't mean weird in a bad way. I love javascript :)
gunderson
+2  A: 

If you can spare 3 hours' time, I'd advise you to have a look at the "The JavaScript Programming Language" videos from YUI Theater. Speaker/teacher is Douglas Crockford, and he will give you a firm JS basis on which you can build.

Regards

Tom

Tom Bartel
+3  A: 

Even though it feels that way sometimes, javascript doesn't have classes, but works with prototypes. You define a prototype, then you can create copies of the prototype.

Starting with:

var animate=function(){};
animate.angular=function(){/*does something here*/};

you can:

var a = new animate();
animate.angular();       // OK
a.circular();            // error: a.circular is not a function

However, if you start with:

function animate(i){};
animate.prototype.angular = function() {};

now you can

var a = new animate();
a.angular();

of course this is more interesting if you have instance variables.

function animate(i) {
    this.x = i;
}
animate.prototype.angular = function() {
    this.x *= 2;
}

var a = new animate(5);
a.angular();
console.log(a.x);    // 10
Ed
+3  A: 

I think you meant to set something equal to new animate() somewhere in your example. Without using new I'll elaborate a little on what happens:

var animate = function(){ console.log(0, 'animate'); };
animate.angular = function(){ console.log(1, 'animate.angular'); };
animate.circular = function(){ console.log(2, 'animate.circular'); };

animate.prototype.angular = function(){ console.log(3, 'animate.prototype.angular'); };
animate.prototype.circular = function(){ console.log(4, 'animate.prototype.circular'); };

Only the first two functions, #1 & #2, are callable from the animate variable.

animate.angular();
animate.circular();

If you create a new animate() you can call the next two, #3 & #4, (but not #1 or #2).

var ani2 = new animate();

ani2.angular();
ani2.circular();

Also, animate() is a function but ani2 is not.

console.log(5, typeof animate);
console.log(6, typeof ani2);
console.log(7, animate());

Although ani2 has already been created, you can add new members to it via the animate.prototype.

animate.prototype.bark = function(){ console.log(8, 'bark'); };
ani2.bark();

The animate variable doesn't inherit form it's prototype however.

console.log(9, typeof ani2.bark);
console.log(10, typeof animate.bark);

Note that ani2 doesn't inherit members applied directly to the animate variable. It only inherits from animate.prototype.

animate.paperclip = function(){ console.log(11, "paperclip"); };

animate.paperclip();
console.log(12, typeof ani2.paperclip);
console.log(13, typeof animate.paperclip);

You can also use the the this keyword inside a constructor function like animate to add instance members to new children.

var Anime = function(a,b){ this.a=a; this.b=b; this.c=console; };
var anime1 = new Anime(14, 'anime1');
var anime2 = new Anime(15, 'anime2');
anime1.c.log(anime1.a, anime1.b);
anime2.c.log(anime2.a, anime2.b);

Anime.prototype.a = 16;
Anime.prototype.z = 'z';

var anime3 = new Anime(17, 'anime3');
anime3.c.log(18, anime3.a, anime3.b, anime3.z, " ", anime2.a, anime2.b, anime2.z, " ", anime1.a, anime1.b, anime1.z);
anime2.z='N';
anime3.c.log(19, anime3.a, anime3.b, anime3.z, " ", anime2.a, anime2.b, anime2.z, " ", anime1.a, anime1.b, anime1.z);

Memory was automatically allocated for a separate instance of anime2.z only because it was modified, anime1 & anime3 still "share" a thrifty unmodified z.

The a, b, and c members are not "communal" in the same way. They were allocated immediately using this in the constructor, new Anime(), (not inherited from Anime.prototype). Also, the a member on the prototype would always be "individualized" by the constructor.

Never forget the new keyword or none of it works like it should. For example, this points to the global object in a constructor called without new.

console.log(20, typeof window.a, typeof window.b, typeof window.c);
var opps = Anime(21, 'zapp');
console.log(22, typeof window.a, typeof window.b, typeof window.c);
console.log(23, typeof opps);

Here's the output. And a second for Tom's suggesting the Douglas Crockford videos!


/*
1 animate.angular
2 animate.circular
0 animate
3 animate.prototype.angular
4 animate.prototype.circular
5 function
6 object
0 animate
7 undefined
8 bark
9 function
10 undefined
11 paperclip
12 undefined
13 function
14 anime1
15 anime2
18 17 anime3 z 15 anime2 z 14 anime1 z
19 17 anime3 z 15 anime2 N 14 anime1 z
20 undefined undefined undefined
22 number string object
23 undefined
*/
machine elf
+1  A: 

Others have mentioned Douglas Crockford already, check Prototypal Inheritance on his site for more information. For contrast see Classical Inheritance.

Like gunderson, I also recommend Crockford's (slim) book JavaScript: The Good Parts.

artlung
+2  A: 

Prototype-based OOP gives you freedoms Classy OOP doesn't.

If you really want to learn about it, like many said, read Crockford's explanations, you won't get any better resource than that.

If you want quick benefits:

var Building = function() {
    this.openDoor = function() {};
};

var House = function() {
    this.closeDoor = function() {};
};
House.prototype = new Building();

var a = new House();
a.openDoor();
a.closeDoor();

Defining objects (which will represent what classes are in other languages) like this is WAY too nasty, so I will include a little tip in my answer:

Best way to build your system, is under a global namespace chosen by you, for example:

if (typeof MYAPP === 'undefined' || !MYAPP) {
    var MYAPP = {};
}

function New(className, classBody) {
    // This adds your "classes" to this MYAPP namespace so they can be instantiated
    // You need some magic here, so have fun with this function
}

New('Building', function() {
    this.openDoor = function() {};
});

New('House', function() {
    this.prototype = new MYAPP.Building();
    this.closeDoor = function() {};
});

// Now you can do the same as before but your application is cleaner :)
var a = new MYAPP.House();
a.openDoor();
a.closeDoor();

Cheers.

leomdg