Matt Snider JavaScript Resource

Understanding JavaScript and Frameworks

Saturday, August 4, 2007

Module Pattern Template

So, as most of you know, I have been swamped with work lately. However, I promised myself that I would get you all a detailed article by the end of this week, regardless of how busy I am. In the near future I want to get back into comparing the different Frameworks, however, those articles take 5-10 hours to write and I just do not have that much time today. Instead, lets discuss the Module Pattern some more. In this article, I am going to show you my template for organizing my module patterns and discuss some details and gotchas about it.

First, why use the module pattern? The module pattern is best used with objects that are instantiated once (or very few times) per page. The module pattern does not leverage the power of prototype based OOP as the Functions are not being attached directly to the prototype of the Object. Instead you are instantiating a function and creating a closure thereby enclosing a self contain Object, which is more expensive in CPU cycles and memory when called frequently. I use the module pattern for 3 reasons, when writing: widgets, managers (like an Event Manager), and business logic for pages.

Here is an example of a module pattern that I use for managing the business logic of a page, which is the business need that I use the Module Pattern for most frequently:

Template.js

/** * Copyright (c) 2007, Matt Snider, LLC. All rights reserved. * Version: 1.0 */ /** * The PageName class manages the business logic of the PageName page * @namespace Core.Biz * @class PageName * @dependencies library */ Core.Biz.PageName = function() { // // Object namespace; private variables go here // var self = null; // internal object to reference public self // // Module dom namespace; id references to the DOM and other DOM objects go here // var dom = { }; // // Module evt namespace; event callbacks go here // var evt = { }; // // Module req namespace; AJAX request and callbacks go here // var req = { }; // // Public methods and constants // return { /** * The DOM selector class to identify example DOM elements * @property CLASS_EXAMPLE * @type String */ CLASS_EXAMPLE: ‘example’, /** * Some publicly available method */ publicDoSomething: function() { }, /** * initialize the object, put elements here that use global object elements (like static variables) */ init: function() { // initialize DOM references that are not null for (var k in dom) { if (isString(dom[k])) { dom[k] = $(dom[k]); } } self = Core.Biz.PageName; } }; }();

In this example ‘Core.Biz.PageName’ will create this object:

Core.Biz.PageName = { CLASS_EXAMPLE: String, publicDoSomething: Function, init: Function }

The object will be created when your page loads, but the internal objects that require the DOM should be put inside the ‘init’ Function and that function should be called when the document is ready (for best performance) or when window loads (if you do not use a powerful enough Event management Toolkit). Inside the closure Function, I generally create 3 namespaces (unless one is not needed), which helps me organize and manage my Object, these are: dom, evt, and req.

The ‘dom’ namespace will generally have the String IDs that I use to find elements on a page, or null, if I am going to initialize the DOM node reference through some other means in the ‘init’ Function. That namespace might look something like this:

var dom = { body: ‘projectBody’, someAnchor: ‘link1′, someOtherElem: null };

By putting this namespace first all other functions of the object can reference these elements. The ‘init’ function will automatically call document.getElementById (or equivalent depending on your Framework) on any String you defined in the namespace, so thereafter the Object members will reference DOM nodes. I would define ’someOtherElem’ in the ‘init’ Function as well, maybe by calling “dom.someOtherElem = dom.body.getElementsByTagName(’div’)[0];”.

The variable ’self’ is an internal shortcut to reference the public object. Initially ‘null’, you assign the ‘Core.Biz.PageName’ Object to it during the ‘init’ Function. Since references to ’self’ will not be evaluated until those Functions are called, it is safe to do use this Object as long as you run the ‘init’ Function first. Using ’self’ helps to prevent memory leaks or circular logic, and you do not have to worry about managing ‘this’. I also use it if I need to call an internal method that is not yet instantiated. For example you would not normally be able to access methods in the ‘req’ namespace from ‘evt’ because ‘req’ is declared after ‘evt’. However, as shown in the ‘evt’ namespace discussion below, you can circumnavigate this issue, by calling a public method on ’self’.

The ‘evt’ namespace will contain all non-trivial event callback functions for events attached to DOM nodes. These events should be attached in the ‘init’ Function after you have finished declaring your ‘dom’ namespace. If using YUI it might look something like this:

var evt = { onAnchorClicked: function(e) { var targ = YAHOO.Util.Event.element(e); CBP.publicDoSomething(); } }; … init: function() { … YAHOO.Util.Event.addListener(dom.someAnchor, ‘click’, evt.onAnchorClicked); … }

The ‘req’ namespace will contain all AJAX request and callback functions. I find this namespace helpful, because I can organize all my AJAX logic in the same place. If you do not use AJAX in your application or the Object does not manage AJAX, then omit this namespace.

The ‘init’ Function will be used to initialize elements of the Object that require the DOM. The first loop is included to do the automatic conversion of the ‘dom’ namespace ID references into DOM nodes. I then declare the ’self’ Object so that any internal references to the public Object are readily available. Thereafter, I will attach events, AJAX, and initialize other widgets used by the page. If you are using YUI I recommend the following line of code be included right after ‘Core.Biz.PageName’ is initialized to fire the ‘init’ Function as soon as the DOM is ready and before the window is completely loaded:

YAHOO.util.Event.onDOMReady(Core.Biz.PageName.init);

That about wraps things up. This is a complicated subject and if you have any questions, do not hesitate to ask. Also, I would be interested in hearing about how you organize your module pattern objects and any pointers you might have.

posted by Matt Snider at 4:26 pm  

Thursday, August 2, 2007

Ext JS 1.1 Released

The Ext team have released a new version of their Framework. I have mentioned this Framework before, because they have developed their package to support other Frameworks (such as YUI, Prototype, and Dojo). A few new features have been added, but more importantly the documentation has been greatly improved and the Framework is completely standalone, although it still supports extending other Frameworks. Find out more at Extjs.com:

Ext JS 1.1

posted by Matt Snider at 10:22 am  

Thursday, July 26, 2007

Detect when DOM Element is in viewport

I just read a good article by Dustin Diaz (formerly of YAHOO) about detecting where elements are in regards to scrolling and the viewport. It is simple and very powerful, so I thought I would share it with you. Also, I am overworked right now and have not had time to write any material of my own :(.

element-scroll-into-view

posted by Matt Snider at 3:49 pm  

Sunday, July 8, 2007

How to improve YUI hasClass method

On my project, Mint, I have reached a point, where the code is mostly stable and decided to start looking for ways to improve the performance. I checked out various JavaScript profiling techniques, deciding that the Firebug profiler was probably the most accurate. I then started profiling the different pages and realized (to my surprise) that a lot of time spent in the ‘YAHOO.util.Dom.hasClass’ method. I use ‘getElementsByClass’ method a lot, so this method is hit frequently. Therefore, since ‘hasClass’ was used the most, any performance improvement to that method would generally improve the performance of Mint.

Here is the YUI method found in dom.js:

         /**
         * Determines whether an HTMLElement has the given className.
         * @method hasClass
         * @param {String | HTMLElement | Array} el The element or collection to test
         * @param {String} className the class name to search for
         * @return {Boolean | Array} A boolean value or array of boolean values
         */
        hasClass: function(el, className) {
            var re = new RegExp('(?:^|\s+)' + className + '(?:\s+|$)');

            var f = function(el) {
                return re.test(el.className);
            };

            return Y.Dom.batch(el, f, Y.Dom, true);
        },

So what is taking a long time here? creating and executing the regular expression.

How do I improve this? replace the regex with something faster or find a way to create something that is already instantiated before this method is hit

I first choose to replace the regular expression (regex) with a call to ’split’ method, of String, on the DOMElements ‘className’. I noticed about a ‘0.5′ millisecond (ms) response time improvement to the execution time of the ‘hasClass’ method and almost a ‘0.1′ ms improvement to the whole function stack. The ‘0.05′ ms improvement is because converting the ‘className’ into an array using ’split’ is that faster than creating a new regex. Also, the execution of ‘batch’ and ‘f’ Functions are improved a combined ‘0.05′ ms, because executing the ‘indexOf’ method on a small array is faster than ‘test’ method of the regex. So, replacing the regex with an array does improve the execution time by a reasonable amount. It will be hard to notice the improved performance on any data set < 1000, however, it is always faster, so you are not hurting by using this change with smaller data sets.

Here is what I came up with:

 /**
		 *  Determines whether an DOMElement has the given className
		 *  @param  el {String/DOMElement/Array}        The element or collection to test
		 *  @param  className {String}                  The class name to search for
		 *  @return {Boolean/Array}                     A boolean value or array of boolean values
		 */
		hasClass: function(el, className) {
			var re = el.className.split(' ');
			var f = function(el) {
				return -1 != re.indexOf(className);
			};
			return Y.Dom.batch(el, f, Y.Dom, true);
		},

I compiled all my tests into Table 1A. The table shows the ms of ‘owned time’ (time the execution thread was actually in that method) that each function had. I setup a simple page with 3 div elements, one without a class, one with a single class, and one with several classes. I then grabbed one element at a time and executed ‘hasClass’ on it 1000 times. I did this 5 times, then took the average of those runs and recorded it as a trial. So each trial consists of 5000 calls to ‘hasClass’. I then modified the ‘hasClass’ method and repeated. ‘hasClass’ calls ‘batch’, which calls the ‘f’ Function, so I have included their ‘owned time’ as well. I did not throw out outliers as I was only proving that the Array method is faster than the Regex method and not trying to accurately estimate how much faster.

Table 1A:
http://spreadsheets0.google.com/ccc?key=pJL0oH5TVaRjjog0qePS2bQ&hl=en_US

Something this data shows that warrants further investigation is: why does ‘indexOf’ method execute marginally faster on an array with a single element versus an Array with no elements? If you look at the average runtime of the ‘f’ Function using the new method, the runtime of ‘f’ is slightly higher when there is no class than when there is one. I produced two more sets of data for each case and got comparable results.

I would have liked to try an approach that moves the instantiation completely out of the method, but I couldn’t think of a good technique. If you have any ideas how to further improve this, let me know.

posted by Matt Snider at 3:56 pm  

Monday, July 2, 2007

jQuery, iFirebug, and iPhone Programming

Some important events happened in the JavaScript community this past week: new jQuery release, Joe Hewitt wrote an iPhone extension for Firebug, and developing for the iPhone is getting a lot of hype. Here are some articles that do a great job covering this:

posted by Matt Snider at 2:31 pm  

Friday, June 15, 2007

Introducing Core JavaScript File and Namespace

Today’s post is going to introduce the file that provides the core functionality for my personal JavaScript Framework. I have already introduced several parts of the file, such as type detection and cookie management. You can view the code @ http://mattsnider.com/corejs.

The first thing you’ll probably notice is that I comment everything. This is the most important part of programming, and often overlooked in Web Applications. You almost can not have enough comments in any given file. In my case, I write so much code and work on so many different projects on a daily basis that without comments, I would be lost. But beyond that, if I wrote a file (of any reasonable length) 4 months ago and revisit it now, I am probably going to have to reacquaint myself with what it is doing (and most likely improve it a little). Figuring out what is going is exponentially faster when you comment your code. It’s best to comment: all functions, large blocks of code, anything that might be confusing, and all hacks.

Now, back to ‘Core.js’. What is does this all do? First, you will see a section that sets up some W3C standard constants, which should be attached to the ‘document’. These constants will be used anytime you want to compare the ‘nodeType’ of a DOMElement. Older browsers and IE do not define these by default, so this block of code fixes that. Note that I do not use browser detection to figure this out, but instead see if the variables are defined in the ‘document’ namespace. Always try not to use browser detection if you can.

/**
 *  W3C DOM Level 2 standard node types; for older browsers and IE
 */
if (null == document.ELEMENT_NODE) {
	document.ELEMENT_NODE                   = 1;
	document.ATTRIBUTE_NODE                 = 2;
	document.TEXT_NODE                      = 3;
	document.CDATA_SECTION_NODE             = 4;
	document.ENTITY_REFERENCE_NODE          = 5;
	document.ENTITY_NODE                    = 6;
	document.PROCESSING_INSTRUCTION_NODE    = 7;
	document.COMMENT_NODE                   = 8;
	document.DOCUMENT_NODE                  = 9;
	document.DOCUMENT_TYPE_NODE             = 10;
	document.DOCUMENT_FRAGMENT_NODE         = 11;
	document.NOTATION_NODE                  = 12;
}

An example of how you might use this is:

var isTextNode = function(node) {
	return document.TEXT_NODE == node.nodeType;
}

So, if you pass a DOMElement into the Function ‘isTextNode’, it will return ‘true’ if the node is a text node.

The next block of code defines the ‘Core’ Object and namespace. I say namespace, because the Core Object will be the global Object that the Framework will be attached to. I use the ‘module’ pattern as coined by Douglas Crockford to create my Core Object. This gives me the freedom to create ‘private’-like variables: ‘debugLevel’ and ‘clientInitFunc’. Debug level is a variable I use to differentiate between production and development messaging. ‘clientInitFunc’ also uses the ‘module’ pattern, creating the template that will evaluate the user’s browser and OS. This is also a logical place to attach cookie management Functions. Here are some references for these methods:

Again, let me reiterate, “DO NOT USE BROWSER DETECTION, UNLESS ABSOLUTELY NECESSARY”. That said, the two browsers you might have to detect most often are IE and Opera, so I have two shortcut methods: ‘isIE’ and ‘isOpera’ that compares against the Core.browser variable.

The next statement returns the constants and methods attached directly to the Core namespace. These include additional namespace variables: Biz, Debug, Client, Widget, and Util. Each of these have been commented to explain what to use them for. I frequently use Core.Client and therefore have attached the Client Object created during the initialization of the Core Object to Core.Client for ease of access.

The next two Functions are helper methods that did not belong in the global namespace, nor any other namespace, so I have attached them to Core. ‘deepCopy’ copies an Object or Array and all of its member Objects and Arrays until all children have been copied. I need this from time-to-time to prevent Objects from being passed by reference, when I do not want a Function to modify the passed element. ‘getImage’ is a short-cut method to create an ‘Image’ instance and then attach the URI src to the image Object. This allows pre-caching of images and simplifies code.

‘getDebugLevel’ is used to retrieve the debugLevel variable. I have designed it this way, because I want to ensure that I (or a hacker) can never update the debug level directly once the script is loaded into the page. Lastly, the ‘init’ Function initializes some internal ‘Debug’ variables and initializes the Core.Client Object.

Now, the ‘emptyFunction’ Function and ‘Class’ Object are copies of similar elements in the ‘Prototype’ Framework. They probably should not be in the global namespace, but i have not had a good enough reason to move them yet. ‘emptyFunction’ is handy, because it allows you to supply a generic do-nothing method. The ‘Class’ Object, can be used to create ‘Prototype’ Objects, but is something I try to stay away from. I have only one instance in the Form Validator that benefited from this design pattern, and so it remains. Also, the amount of code is negligible and some people may prefer to build ‘Prototype’ Objects.

The file closes with all the type detection functions. These are described in detail for the Type Detection article.

Although, short and sweet, this file has some complex ideas and may take a little getting used to. Please let me know your thoughts and any possible improvements, especially with the ‘deepCopy’ method.

posted by Matt Snider at 8:57 am  

Thursday, June 14, 2007

More on Frameworks

A List Apart Magazine recently posted an article on Frameworks. This mostly talks about CSS Frameworks, but does a great job describing Frameworks as a concept.

frameworksfordesigners

posted by Matt Snider at 10:50 am  

Monday, June 4, 2007

Prototype vs. YUI Round 2: I love $

Probably the most commonly needed Function for a Web Application Developer (WAD), is document.getElementById. As you will most certainly need to retrieve HTMLElements from the DOM by the element’s ID. Using ‘getElementById’ is perfectly valid, but has potential drawbacks such as:

  • ‘document.getElementById’ is verbose
  • multiple elements cannot be retrieved with one simple call
  • Some browsers, especially old browsers, do not support this call.
  • The more IDs you use in your document, the longer the seek time of this method.

As the title says, I am in love with the ‘$’ (I am also in love with money, in case you were wondering). The ‘$’ function was first developed by Prototype (and IMHO is Prototype’s greatest contribution to the JavaScript community). My love for the ‘$’ function is two-fold:

  • It is elegant, simple, and concise
  • Using the $ symbol as a shortcut, should be safe and most people did not know this was possible (so it is cool)

But beyond those points, let us really consider what an element retrieval function should and should not do:

Dos

  • Process lists of elements as well (both arguments list and arrays)
  • Handle all x-browser issues and old browsers, or degrade nicely
  • Caching. It is faster to maintain an array for all previously retrieved IDs, than to retrieve them each time
  • Do nothing when passing an HTMLElement (it should be acceptable to use ‘foo= $(foo)’, just to ensure that is an HTMLElement

Do Nots

  1. Attach a bunch of variables and/or functions to the HTMLElement*
  2. DO NOT do item 1

* This is my single biggest gripe with pretty much all JavaScript Frameworks. Why do each of my HTMLElements need a slew of helper functions that are already available to me elsewhere? I am not that lazy and frankly it is expensive to add Function references to every HTMLElement that I retrieve. Plus: you increase your chances of circular references causing memory leaks, different Frameworks and Toolkits may conflict with each other, and some future version of JavaScript may define that same function.

That said, let us move on to comparing YUI ‘YAHOO.util.Dom.get’ vs. Prototype ‘$’. I have created an simple rating system that I will be using for this and future discussion. I will walk through each Function and rate them using a point system, where you get plus points for each ‘Dos’ (ex. +1) and minus points for each (ex. -3) ‘Do not’ used. Here is the newest prototype ‘$’ function from version 1.5.1:

Prototype ‘$’ Function

function $(element) {
if (arguments.length > 1) {
for (var i = 0, elements = [], length = arguments.length; i < length; i++)
{elements.push($(arguments[i]));}
return elements;
}
if (typeof element == 'string')
{element = document.getElementById(element);}
return Element.extend(element);
}

I am happy in this release that they allow you to pass a list of arguments and retrieve multiple HTMLElements at the same time (+1). I am going to give the Function (+1) for using the ‘$’ name. However, it does not allow you to pass in an array as an argument, which is important because sometimes you will have gotten your lists of IDs from a method that created an array of Strings (+0) and will be required to use a work around to pass these Strings into this Function. The Function does retrieve arguments that are Strings and returns the non-string Objects (hopefully HTMLElements) (+1). Unfortunately, the method attaches needless and dangerous methods to the HTMLElement that you pass or retrieve (-3). It does not work with old browsers or degrade well (+0) and has no built in caching system (+0). This gives Prototype an overall rating of (0), which is pretty poor.

YUI ‘YAHOO.util.Dom.get’ Function

get = function(el) {
if (!el) { return null; } // nothing to work with
//
if (typeof el != 'string' && !(el instanceof Array) ) { // assuming HTMLElement or HTMLCollection, so pass back as is
return el;
}
//
if (typeof el == 'string') { // ID
return document.getElementById(el);
}
else { // array of ID's and/or elements
var collection = [];
for (var i = 0, len = el.length; i < len; ++i) {
collection[collection.length] = Y.Dom.get(el[i]);
}
//
return collection;
}
//
return null; // safety, should never happen
}

As with Prototype you are allowed to pass multiple arguments at the same time (+1) and although you cannot pass in an array of arguments, you can pass an argument that is an array, which is more powerful (+1). There is no ‘$’ function short-cut (+0), but I always map this method to the ‘$’ symbol anyway, so it is really not a big deal. The function does retrieve arguments that are Strings and returns the non-string Objects (hopefully HTMLElements) (+1). It does not attach needless/dangerous methods to the HTMLElement (-0). Unfortunately, it does not work with old browsers (+0) (it does degrade well, by return ‘null for unexpected values) and has no built in caching system (+0). This gives YUI ‘get’ Function an overall rating of (+3), which is average, however, if you map the ‘$’ symbol to the ‘get’ Function, then it would bump the rating up one, making YUI ‘get’ Function slightly above average.

Therefore, using my metrics, YUI’s ‘get’ Function is better than Prototype’s ‘$’ Function. I recommend using the YUI one and mapping it to a function named ‘$’ in the global space, emulating Prototype. Not only will you be able to use the concise ‘$’ symbol to retrieve HTMLElements, but doing this will allow you to use YUI with some of Prototype-based Toolkits and Frameworks, without the down-side of using Prototype. Obviously, there is a little room for improvement, as there is no caching yet and there is not a work around for browsers that do not support the ‘document.getElementById’ method. However, 95%+ of internet users (with JavaScript enabled) will have a browser that supports ‘document.getElementById’ (and this number increases every year), and caching gives a marginal improvement only when repeatedly retrieving the same element using the ‘get’ Function (and may be detrimental to performance if you use your own caching system).

Lastly, I want to leave you with a slightly modified YUI ‘get’ function that I use. I use YUI’s namespace approach and attach everything to an Object named ‘Core’, which I later shortcut key methods (such as ‘get’) as part of my JavaScript build process. Here is my method (it will be made available as part of my Core.Util.Dom package after I have further discussed DOM manipulation:

Core.Util.Dom.get: function() {
var multiArgs = (1 < arguments.length); // arguments is not an Array
var elem = multiArgs? arguments: arguments[0];
//
// nothing to work with
if (! elem) {
return null;
}
//
// assume is a DOMElement, but could be any Object
if (! isString(elem) && ! isArray(elem) && ! multiArgs) {
return elem;
}
//
// ID
if (isString(elem)) {
return document.getElementById(elem);
}
// array of ID's and/or elements
else {
var collection = [];
for (var i = 0, j=elem.length; i collection[i] = Core.Util.Dom.get(elem[i]);
}
return collection;
}
}

posted by Matt Snider at 3:08 pm  

Wednesday, May 23, 2007

Fun With Forms

I have been spending a lot of time improving my Form Utility Framework and wanted to share a little with you. First, we should address the reasons why we would want a Form Utility Framework. Here are some of my reasons:

  • Serialization for ajax - converts forms into key/value pair string (ex. “&key1=value1&key2=value2…”), often used with AJAX requests
  • Validation - rather than writing code to manually validate each form, i developed a system that allows you to add any number of validators for any number of fields in a form
  • Abstracted Value Retrieval - a universal method used to always retrieve the value of an input whether it is an input, a select, or a textarea HTML tag, so you do not need to remember how or worry about x-browser issues
  • Helper Methods - functions such as ones that: retrieve inputs by name, gets the first visible input of the form, disable/enable forms, etc.

Since every page will not always have a form and the Form Utility Framework will not always be used, try to keep it as lightweight as possible; it is easy to bloat your Utility with functions that you infrequently or never use.

If you find yourself using a Form Utility Framework, you might run into this problem that I had. I had not used the ‘reset’ HTMLInput type in a long time and found myself wanting to use it to clear the fields in a form. In the past that had always worked for me, but if you are using a server-side scripting language to set the values of the form fields when the page load, then you will find that reseting the form does not clear it. Instead, it reverts to the values that the form had when the page loaded. This is because in most modern browsers, when initializing a page that sets the form field value attribute, those values also are set the the fields defaultValue attribute. According to the W3C standards, the defaultValue attribute is what should be used for form reseting. Therefore, if the form fields had no values when the page load, then the reset function worked as I wanted, otherwise, it did not. To defeat this I created my own method ‘clear’ that iterates through the different form field elements and removes their values.

if (! Form) {Form = {};}
Form.clear = function(form, ingore) {
var inputs = form.getElementsByTagName('input');
// inputs
for (var i=0, input; input=inputs[i]; i++) {
if (-1 == ingore.indexOf(input.type.toLowerCase())) {
input.value = "";
if (input.checked) {input.checked = false;}
}
}
// textareas
if (-1 == ingore.indexOf('textarea')) {
inputs = form.getElementsByTagName('textarea');
for (var i=0, input; input=inputs[i]; i++) {
input.value = "";
}
}
// selects
if (-1 == ingore.indexOf('select')) {
inputs = form.getElementsByTagName('select');
for (var i=0, sel; sel=inputs[i]; i++) {
sel.selectedIndex = 0;
}
}
};

This method is pretty straight forward. Pass the method an HTMLFormElement Object and it will parse through the object’s child nodes by the tag names using the appropriate reset method. Lastly, I tossed in an array called ignore (array of string types that are not to be cleared), because I often don’t want to reset types such as “button”, “hidden”, “submit”, and “reset”. I was thinking, it might be helpful also to have a list of field names to ignore, but I did not have a need for it yet.

posted by Matt Snider at 2:13 pm  

Tuesday, May 8, 2007

Prototype vs. YUI round 1: OOP Architecture

First, let’s make sure we are all using the same Frameworks, because these teams have been cranking out changes faster than you can keep up with them. This discussion will use the stable release of Prototype 1.5.1 and YUI 2.2.0. We will be analyzing the speed of creating small dummy objects to get an idea of which approach is better. Before, starting, I’d like to take a moment to thank the YUI guys for following rigorous coding standards and well documenting their code. I don’t know what high-horse the Prototype guys are riding, but there code is sloppy and it doesn’t compress well. Consequently, I have taken some time to clean up the prototype code (added semi colons, {} all statements, and ran jsmin): uncompressed and minified.

In the Prototype Framework you create Objects using the ‘Class.create()’ method. This basically attaches a function to your Object, that will execute the ‘initialize’ method you attach to the Object’s ‘prototype’ Object with all the variables you passed into the constructor. That’s a mouth full and a little hard to grasp textually, here is an example:

var PrototypeObject = Class.create();
PrototypeObject.prototype = {
initialize: function(v1, v2) {
// my initialization code
this.v1 = v1;
this.v2 = v2;
},
testFunction: function() {
alert(this.v1);
}
};
// to use
var someVariable = new PrototypeObject (v1, v2);
someVariable.testFunction();

The ’someVariable’ Object will contain all the functions and variables attached to ‘PrototypeObject.prototype’, including the initialize function. When I used this OOP Architecture, I often design the initialize function in such a way that it could be called again later to reset the object. This example illustrates how you can pass variables into the constructor and set them internally so that other function, like ‘testFunction’ can access the v1 variable, because it is attached to the ‘this’ keyword Object. You can extend these Object indefinitely, declare addition Objects inside, and do any number of customizations. However, I find that these Objects become larger than they need to be and are often difficult to follow.

In the YUI Framework you create Objects by executing a Function that contains ‘private’ like variables accessible only in the scope of the Function or through Closures in a returned Object. Again, difficult to describe textually; here is an example:

var YUIObject = function(v1, v2) {
var v1 = v1;
var v2 = v2;
return {
testFunction: function() {
alert(v1);
}
}
};
// to use
var someVariable = new YUIObject (v1, v2);
someVariable.testFunction();

So, this Object basically does the same thing as the PrototypeObject. The constructor call, instantiates the Function YUIObject, passing in two variables. Inside the scope of the Function those variables are redeclared to attach to the Function scope. Then the function returns an Object that ’someVariable’ will be set to. The ‘testFunction’ has access to v1 and v2 inside the Function scope because of Closures. For me this Architecture is easier to read and simpler to use, so I have built most of my Objects this way. The downside is that Closures leak memory like sieves in IE, if you are not careful.

To wrap up our discussion, I have created a page where you can compare the run times of the different instantiation. Unfortunately, JavaScript run-times aren’t very accurate and depend on the idle time of your computer. Having run it many times though in many browsers, I can summarize that on average the initialization of the YUI-type Objects is 35-45% faster than the Prototype Objects. Although, we’re talking about fractions of a millisecond, so you’re only going to notice this when you are creating thousands of Objects.

http://mattsnider.com/PrototypeVsYUITest.html

posted by Matt Snider at 8:20 pm  
« Previous PageNext Page »

Powered by WordPress