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...
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 */
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
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.
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
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.