A Wired.com user account lets you create, edit and comment on Webmonkey articles. You will also be able to contribute to the Wired How-To Wiki and comment on news stories at Wired.com.
It's fast and free.
Retrieve Sign In
Please enter your e-mail address or username below. Your username and password will be sent to the e-mail address you provided us.
Welcome to Webmonkey
- edit articles
- add to the code library
- design and write a tutorial
- comment on any Webmonkey article
Sign In Information Sent
Make OOP Classes in JavaScript
/skill level/
/viewed/
Contents |
Introduction
Anyone who has tinkered with the prototype model in JavaScript to produce classes as we know them in other languages has generally left web programming all together and taken up a career as a bus driver. There are a coterie of frameworks designed specifically to address this problem. But what if you're not wild about frameworks, preferring instead to build tools specifically suited to your needs and style? For you, there is some black magic that can revolutionize the way you interact with the current JavaScript standard (someone please thaw me out when the 2.0 standard is implemented in June 2134, or whatever the projected date is).
What you'll need
a text editor and a web browser
Steps
Meet the encapsulated JavaScript function! More than a function, less than a traditional OOP class, but super useful and a cinch to make. All you really need is:
function someObj() { function someMethod() { alert('boo'); } } o_obj = new someObj();
As the name suggests, and encapsulated JavaScript function is just that - one function encapsulated within another. If you dropped this into a page and ran it in the browser, you'd have a big load of nothing. But whip out the "this." keyword, and things get interesting. In JavaScript "this." refers to the owner of an object, be it variable, function, or DOM element. For our purposes, "this." refers to an instance of an encapsulated function.
Let's change the previous code a bit.
function someObj() { this.someMethod = someMethod; function someMethod() { alert('boo'); } } o_obj = new someObj();
Now we've added the line this.someMethod = someMethod, which makes an individual and public reference to someMethod(). We might also make this neater by combining this.someMethod with the function declaration, as follows:
function someObj() { this.someMethod = function() { alert('boo'); } } o_obj = new someObj(); o_obj.someMethod(); //alerts "boo"
If I execute the above script in a browser, I'll get an alert message with "boo" in it. Of course, what's good for the function is good for the variable, as shown in the following example.
function someObj() { this.publicVar = 'public'; var privateVar = 'private'; this.someMethod = function() { alert('boo'); } } o_obj = new someObj(); alert(o_obj.publicVar); //alerts "public" o_obj.publicVar = 'new'; alert(o_obj.publicVar); //alerts "new" alert(o_obj.privateVar); //alerts nothing or an error, depending on the browser
Execute this in the browser, and you might get chills. Holy crap, we have public and private variables. You just have to remember that all references to a public variable within encapsulated functions must be preceeded by "this." or you'll end up accidentally creating a totally separate variable. And to bring it full circle, private methods are made possible like this...
function someObj() { this.publicVar = 'public'; var privateVar = 'private'; this.someMethod = function() { alert('boo'); someOtherMethod(); } // Private method. (Due to closure, this need not be declared before // someMethod although someMethod uses it.) var someOtherMethod = function() { alert('indirect reference'); } } o_obj = new someObj(); o_obj.someOtherMethod(); //will throw an undefined function error o_obj.someMethod(); //alerts "boo" followed by "indirect reference"
the above example shoud that function not given a "this." pointer is a defacto private method.
Here you will probably pause to realize that this particular black magic seems too good to be true. "Show me ye olde classical inheritance!" you cry. Here's where the limits to this sorcery are exposed. Classical inheritance doesn't come naturally to JavaScript. With a little coaxing you can get something equally useful, especially if you wish to build interrelated but independent libraries of encapsulated code following all of the other OOP rules we know and love. Consider the following:
function someObj() { this.o_otherobj; this.doOtherObj = function() { this.o_otherobj = new otherObj(); } } function otherObj() { this.otherMethod = function() { alert('over the rainbow'); } } o_obj = new someObj(); o_obj.doOtherObj(); o_obj.o_otherobj.otherMethod(); //alerts "over the rainbow"
This time, we have two independent and encapsulated functions, one essentially encapsulating another and providing a public interface. Those wondering if they can have a constructor function should note that in the above example that,
function someObj() { this.o_otherobj; this.doOtherObj = function() { this.o_otherobj = new otherObj(); } }
could just as easily have been,
function someObj() { this.o_otherobj = new otherObj(); }
and gotten the same result. Any code not encapsulated within that second-tier of functions is executed when the object is instantiated, mimicking constructor behavior.
Remember that standard variable scoping rules apply within encapsulated functions. Any variable declared in a second-tier function will become global to the whole class, so you have to be as careful with your namespace as you would be otherwise.
As a caveat, this method comes with more overhead than the typical usage of Javascript prototype to declare classes as each methods are duplicated across objects.
Alternate methods
For a more direct approach, you can copy the following into an .html file and play with it live in your browser.
<html> <head> <script type="text/javascript"> function jsObj() { this.m_public; //public variable this.o_griffins; //public variable this.doGriffins = doGriffins; // public method this.doSayItOutLoud = doSayItOutLoud; // public method this.getPrivateVariable = getPrivateVariable; // public method var m_private; // private variable alert('It\'s alive!'); //"constructor" function doSayItOutLoud(m_var_fp) { // this method is public because it has a "this." pointer this.m_public = m_var_fp; if (confirm(m_var_fp + ' is public knowledge. Do you wish to save it?')) { setPrivateVariable(m_var_fp); } } function doGriffins() { this.o_griffins = new familyObj(); } function getPrivateVariable() { //this method is public because it has a "this." pointer alert('The private variable is ' + m_private); } function setPrivateVariable(m_var_fp) { //this method is private because it has no "this." pointer m_private = m_var_fp; } } function familyObj() { this.doMeg = doMeg; this.doPeter = doPeter; this.doStewie = doStewie; function doMeg(m_var_fp) { alert(m_var_fp + '. Is that what you want me to say?'); } function doPeter() { alert('Bird, bird, bird, bird is the word!'); } function doStewie() { alert('Thoughtless heathen programmer only gave me an alert().'); } } var o_jsobj = new jsObj(); </script> </head> <body> <div> For a good time, paste these snippets into your address bar! <ul> <li>javascript:o_jsobj.doSayItOutLoud('Yo!');</li> <li>javascript:alert(o_jsobj.m_public);</li> <li>javascript:alert(o_jsobj.m_public);</li> <li>(if you saved a variable) javascript:o_jsobj.getPrivateVariable();</li> <li>javascript:o_jsobj.getPrivateVariable();</li> <li>javascript:o_jsobj.doGriffins();</li> <li>javascript:o_jsobj.o_griffins.doMeg('Booger');</li> <li>javascript:o_jsobj.o_griffins.doPeter();</li> <li>javascript:o_jsobj.o_griffins.doStewie();</li> </ul> </ul> </div> </body> </html>
- This page was last modified 22:41, 28 November 2008.
/related_articles/
Special Offer For Webmonkey Users
WIRED magazine:
The first word on how technology is changing our world.