Myopia and the Opera 10 User Agent String Friday, 29 May 2009

Opera has conceived a silly tactic planned for Opera 10 user-agent string.

The problem is that there are scripts that expect the browser major version to single-digit and will fail if it is not.

Since "10" not a single digit, these scripts fail.

Opera has mitigated that problem by changing the user-agent to 9.80 and publishing the following warning:

Browser sniffing ? unless you?re writing a web stats application ? is always a bad idea. It?s a misguided attempt to send different content to different user agents. This is never scalable ? you can?t change every website you?ve ever made every time a new browser version comes out. It is also not future-proof, as highlighted by this article.

Ineffectual and meaningless little blurb there. Those badly written sites that used (poor) browser detection will not break from Opera. Opera spoofing their own user-agent string helps reaffirm the misconception that the authors' browser detection worked. Posting up a little warning that not everyone will read does not make an example.

The blurb states that Browser detection is used "to send different content to different user agents". Not always true. In fact, browser detection is more often used on the client to work around an perceived incompatibility. Since Opera is wrong on that count, it makes the blurb seem even less relevant, as an author who read it might still try to justify or rationalize his approach by saying "but that's not why I used browser detection."

Browser detection scripts cause forwards-compatibility and maintenance problems. However, to not be able to parse out a number is not only not smart, it shows very poor coding skill.

Opera states that version 11 will have "11" in the user-agent string.

My opinion is somewhat in line with Doug's on this one (that Yahoo 360 URL is an awful URL).

If you are a developer, check your code. It really isn't hard to do this stuff correctly. It really isn't.

Where I disagree with Doug is "Opera has been forced to lie."

Opera developers made a decision to lie, as explained by Opera. They were not forced.

An alternative to that choice is for Opera to not cater to badly authored pages and simply let them break.

Breaking sites is bad in the short term because it renders pages unusable. However, it is good in the larger scheme of the web in the long run. By driving home a hard lesson, Opera could teach developers to not use browser detection by providing an historical lesson.

The first sensible opinion on the matter was Hallvord's post from December, 2008., where he pointed out that Bank of America and Live.com failed in Opera 10. The entry describes the reason: Faulty parsing of the User-Agent string, and redirecting to the "not supported page".

You'd think that with the intense development Microsoft has been lavishing on live.com they would have found somebody capable of writing a usable browser sniffer (or ideally a person clever enough to say "wait, we don't really need one - what if we just use feature detection instead?"). Think again..

Of course, Microsoft has been advocating detection "best practices" for years, despite well reasoned arguments to stop doing that (G. Talbot, T. Zijdel).

Opera should be less myopic and stop worrying about breaking badly authored sites. Web developers should be less myopic, and build maintainable, forwards-compatible solutions.

Posted by default at 12:06 AM in Browsers

Function.prototype.bind Thursday, 11 September 2008

Function Binding

A bind function wraps a function in a closure, storing a reference to the context argument in the containing scope.

This allows the bound function to run with a predetermined context.

Variable this

When a function is passed as a reference, it loses its base object. When the unbound function is called, the this value is the global object.

How Binding Works

By storing a reference to the desired object in a closure, this argument can be bound.

In it's simplest form, bind looks like:-

Function.prototype.bind = function(context) {
  var fun = this;
  return function(){
    return fun.apply(context, arguments);
  };
};

Why Binding is Useful

Binding is often necessary when passing function references. For example:-

var updater = {
  fetch : function() {
    alert(this.time++);
  },
  time : 0
};
// setTimeout(updater.fetch, 500);
setTimeout(updater.fetch.bind(updater), 500);

The commented-out call to setTimeout would result in a call to updater.fetch with the global object for the this argument. this.time would be undefined, and this.time++ would result in NaN.

A bind function that does only binding accomplishes a trivial task. In most cases, a closure can just be used where binding is needed.

Binding in the Wild

Most JavaScript libraries handle binding internally. These libraries also include a partial apply for their bind function.

Partial Apply

Partial application is setting parameter values of a function call before it is called. A partial apply function usually looks like:-

/** 
 * Return a function that prepends the 
 * arguments to partial to this call and 
 * appends any additional arguments.
 */
Function.prototype.partial = function() {
  var fun = this,
      preArgs = Array.prototype.slice.call(arguments);
  return function() {
    fun.apply(null, preArgs.concat.apply(preArgs, arguments));
  };
};

This allows us to program in a dynamic, functional, less OO way. For example:

function setStyle(style, prop, value) {
  return style[prop] = value;
}

// Create a setBgColor function from 
// partial application of setStyle.
var setBgColor = setStyle.partial(document.body.style, "background");

// Change the body's background color.
setBgColor("red");
setBgColor("#0f0");

Disadvantage

Partial application requires an extra function call, plus a call to concat for the extra arguments.

Partial application can make debugging trickier, since there is an extra layer of indirection to the real method.

Bind + partial apply can be used to force the this argument of a prototype method to always be the of the instance. This is inefficient and often leads to messy, tangled function decomposition. Libraries that bind every method do so out of ignorance of the language, and are best avoided.

EcmaScript New Language Feature

The forthcoming version of EcmaScript (now called EcmaScript Harmony) will include Function.prototype.bind(context). A native bind should outperform any other bind function.

This was something Peter brought up for EcmaScript 4, but appears to be making way into the revised EcmaScript Harmony.

Mark Miller wrote out a "self-hosted" version of EcmaScript's proposed Function.prototype.bind. It is:-

Function.prototype.bind = function(self, var_args) {
   var thisFunc = this;
   var leftArgs = Array.slice(arguments, 1);
  return function(var_args) {
    var args = leftArgs.concat(Array.slice(arguments, 0));
    return thisFunc.apply(self, args);
  };
};  

After looking at the ES Harmony proposal, and looking at a few versions of bind functions, I decided to write a better one that does exactly what the ES Harmony's bind does, but with greater efficiency than the current libraries offer, and whose, length property was 1.

Although unnecessary, this is a welcome addition to the language. A native bind will outperform any user-defined bind function and will result in fewer closures.

The Rundown

Before I give a critique and rundown, I have a test.

Library Comparison Test

  1. Garrett's Bind

    This bind was, by far, the most efficient in tests #1, and #4, and nearly ties Base 2 in test #2 (Base 2 was about .5 ms faster) . This function requires no additional code or functions.

  2. Base2 bind

    Second performance-wise.

    Requires only a top level _slice function (trivial), and performs a strategy for extra arguments.

  3. Dojo's hitch

    Dojo was fast with pure bind, but slower with partial apply.

    This function requires many other functions and has an additional complication of accepting strings and arguments of different order.

  4. Ext-js Function.prototype.createDelegate

    Performance was slow. This function requires no importing of external functions.

  5. Mark Miller's bind

    Requires no external dependencies. While it gets a 10 for simplicity and aesthetics, this function was not as fast, and for pure bind (no partial apply) was not nearly as fast as it should be.

  6. Prototype's Function.prototype.bind

    Performance was fair. Requires several extra functions + browser detection. The function is used very heavily internally.

  7. Mootools Function.prototype.bind

    Performance times were poor and the results for two tests were wrong. were wrong.

    The entire mootools.js is required for the bind function. The library adds a $family property, and makes other changes to Array.prototype.

  8. YUI 3 bind

    Performance time was fair. The results for test#2 are wrong because the call's arguments are prepended, not appended. This is by design.

    Having the call's arguments prepended is an unusual design decision. It might seem unintuitive, and confuse developers who are used to the more common version, as seen in Prototype, Base2, and the official EcmaScript proposal.

    The amount of code YUI's bind depends on is staggering.

Pass the Parmeźan

Many of the libraries have long chains of function calls. A bind function does not need and should not require the inclusion of several other functions.

Prototype JS requires the $A function, which requires Prototype.Browser to determine which $A function. Browser detection has absolutely no place in Function binding. ($A also calls toArray conditionally, but that will not happen in this case.)

Dojo is almost as bad. Dojo's hitch function has the arguments in reverse order and requires dojo.global, dojo._hitchArgs, dojo._toArray, and dojo.isString.

Mootools has very strewn code. The broken bind function required an additional 108 lines of Mootools.

YUI is the most grandiose. Function YUI.bind(f, o) is found in "oop.js". File oop.js requires over 120k of "prerequisite" files in yui.js and yui-base.js, coming to a total of over 160k. Just for bind. YUI 3 seems to suffer from over-engineering and BUFD, which is typical in waterfall shops.

YUI's 'array' module did not seem to load or evaluate properly, so code from the yui-base file was copy-pasted.

Worth Using?

The best bind functions are fast, do not require other library functions, and are fairly simple.

But is a Bind Function Necessary?

No. Binding can be achieved with an inline closure where it is needed and partial application is not necessary.

Here is example 1, without using bind.

var updater = {
  fetch : function() {
    alert(this.time++);
  },
  time : 0
};
setTimeout(function() { updater.fetch(); }, 500);

A native Function.prototype.bind will allow for cleaner binding, without the need for creating a closure. As native code, it will be faster and more reliable. Function.prototype.bind is not necessary, but is a welcome addition to the language.

Why All the Fuss?

Being aware of what libraries do and identifying and learning from the mistakes of libraries helps developers avoid such mistakes by learning what the library does. Developers do not need the burden of large, tangled, and often buggy library dependencies.

Look at The Code

What the library's bind function does and how the library uses that function internally is a step to take in assessing the library's quality.

A developer can make a more responsible and professional choice by avoiding a library that makes heavy use of a slow-spaghetti bind function.

My Version

The following function is the fastest bind function for pure bind (no partial apply). It is more than three times as fast as Base2, the second fastest bind function tested here.

Here is my version of the bind function.

/**
 * @param {Object} context the 'this' value to be used.
 * @param {arguments} [1..n] optional arguments that are
 * prepended to returned function's call.
 * @return {Function} a function that applies the original
 * function with 'context' as the thisArg.
 */
Function.prototype.bind = function(context){
  var fn = this, 
      ap, concat, args,
      isPartial = arguments.length > 1;
  // Strategy 1: just bind, not a partialApply
  if(!isPartial) {
    return function() {
        if(arguments.length !== 0) {
          return fn.apply(context, arguments);
        } else {
          return fn.call(context); // faster in Firefox.
        }
      };
    } else {
    // Strategy 2: partialApply
    ap = Array.prototype,
    args = ap.slice.call(arguments, 1);
    concat = ap.concat;
    return function() {
      return fn.apply(context, 
        arguments.length === 0 ? args : 
        concat.apply(args, arguments));
    };
  }
};

This function was not included in APE because it was not needed. This function may be used by libraries who wish to continue using a bind function with the benefit of faster performance.

Posted by default at 5:20 PM in JavaScript

Find Element Position Friday, 4 July 2008

Most libraries try provide some functionality for finding an element's position, but fail in all but the simplest cases.

I created a test page to demonstrate the libraries' results of finding an element's position and the time it takes for them to succeed or fail.

Test Page

Compare JavaScript Libraries

Demos are Not Unit Tests

These two demos don't cover all of the complexities in finding an element's position. I didn't even touch tables. There are also complications with padding and border on HTML root element. Unfortunately, IE8b1 doesn't support CSS on the HTML root element (connect id 354453), so it can't be tested.

APE guards against all of the other problems by having a test suite that tests this function with combinations of these cases.

Posted by default at 2:36 AM in JavaScript

Prototype.js - A Review Tuesday, 17 June 2008

I was prompted to do a code review on PrototypeJS. Here is a partial review.

Ten Issues

  • Modification of Host Object - Non standard, doesn't work the same in all browsers. Don't do it!
  • Class.Methods.addMethods -
    • Don't rely on a Function's toString!
    • Don't introduce special behavior based on function Decompilation!
    • The mimicing of class based inheritance through closures to get $super to work requires a significantly augmented scope chain.
  • The Dollar Function - Meaningless and extremely inefficient
  • Element.Methods.visible - misnamed, non-reflective, and buggy/unreliable!
  • More Modifications to Host objects
  • Object.extend - Don't forget the JScript DontEnum bug!
  • $A - Relying on object's toString -
  • readAttribute - Don't rely on Function Decompilation
  • Ajax.Request - Add an Underscore?
  • Broken unescaping of HTML Strings
  • Unnecessary use of with statement
  • Enumerable.include - Strict Equality or 2n with Loose Equality
  • getDimensions - Don't Expect clientWidth to be non-zero in IE
  • cumulativeOffset - doesn't account for borders, scroll offsets, or magic BODY.

Okay, so it's more than 10, and could be even more, but I have to stop somewhere!

Modification of Host Object

A Host object is an object provided by the Host environment, e.g. document, event, an element.

PrototypeJS relies on modifying Host objects as a part of its core approach. There are several problems with this approach.

  • There is no requirement in the EcmaScript specification that requires Host objects to be modifiable (although they usually are).
  • There is no requirement in the EcmaScript specification that the Host environment expose for its Host objects a constructor with an associated prototype.
  • There is no requirement in the EcmaScript specification that objects expose a __proto__ property.
  • There is no guarantee by any standard that there will be a __proto__ property present for an Element or what value, if any, it will hold.

The fact that Mozilla exposes the prototypes of Element, et c is useful in that it allows developers to provide quick patches for new or broken features that lack proper support. However, this feature can't be reliably used for websites that are expected to run in multiple browsers.

Class.Methods.addMethods

Class.Methods.addMethods relies on the approach of calling toString on a function, expecting to get back the source code, as it originally appeared.

if(value.argumentNames().first() == "$super")

A few significant problems:

  • Function.prototype.toString is not required to return the source code of the function

    Function.prototype.toString is guaranteed only to return an implementation-dependent representation of the function as a FunctionDeclaration [or FunctionExpression] (spec errata, has been fixed). (§15.3.4.2).

    Opera mobile follows the spec but takes the liberty of not returning the source code of the function.

  • Even if relying on the name of a function's formal parameter were guaranteed, doing so would make implementation code fragile because it would be impossible for a human (or compressor) to rename it (YUI Compressor bug).
    Object.extend(Function.prototype, {
      argumentNames: function() {
        var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");
        return names.length == 1 && !names[0] ? [] : names;
      },
    

Providing a toString that returns helpful debugging info for objects is generally good advice.

However, the return value of a function's toString cannot be expected to be anything other than a string value. It should not be relied on. Unfortunately argumentNames will choke on a Function object that has a custom toString. This problem will happen in every browser.

Example: Programmer-defined toString Causes Problem with Object.extend

function WidgetFactory(){}
WidgetFactory.toString = function(){ return"[WidgetFactory constructor]"; };

If argumentNames() is called on WidgetFactory, the first call to match() will return null. Then, when split() is called, a TypeError will be thrown.

It would be less error-prone for PrototypeJS to use:

    var names = Function.prototype.toString.call(someFunction);
    

This avoids the mistake of relying on the function instance's toString, however, the approach still has two significant problems:

  1. relies on the source code and named formal parameters of the function on which it is called (problem 1).
  2. Function.prototype.toString is still not required to return the source code of the function Function.prototype.toString is guaranteed to return an implementation-dependent representation of the function as a FunctionDeclaration [or FunctionExpression] (spec errata). (§15.3.4.2), and in fact, Opera mobile follows the spec correctly, but takes the liberty of not returning the source code of the function.

Relying on Function Objects' toString

Do Not Rely on Return Values from Function Objects' toString!

In general, toString should not be parsed or relied upon for data formats. toString is useful for debugging. In October 2007, I pointed out how Dojo and jQuery made this mistake. Hallvord has been writing against relying on Function Decompilation for well over a year, as it resulted in problems in PrototypeJS and jQuery running in Opera Mobile.

The Dollar Function

PrototypeJS is built on the approach of modifying Host objects. This is the cornerstone of the library's problems. The library will try to modify the built-in prototype of an XPConnect wrapped prototype if it assumes the browser can do that. Otherwise, it will add properties to the element itself.

The primary accessor to these modified Host objects is through that dollar function.

Law of Demeter

Each unit should have only limited knowledge about other units: only units "closely" related to the current unit. (LoD).

I'll come back to explain how Prototype.js' modifications to Host objects violate LoD, and the problems associated with that.

What Does $ Do?

  • $ does not have a clearly defined meaning as to what the function actually does.
  • The dollar sign is intended to be reserved for machine-generated code.

PrototypeJS $ function gets an element or array of elements. Calling $ results in a bare minimum of three function calls: $, isString, and Element.extend and a maximum of over 135 function calls.

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

Call Stack of $ (best case)

$ -> isString
  -> document.getElementById
  -> Element.extend

Count of Functions from $ (best case)

$................................+1 
 +--isString.....................+1
 +--document.getElementById.....(+1) (when isString(element) is true) 
 +--Element.extend...............+1 
    ~--Prototype.K||(anonymous)...0 (alias, second time, only. Prototype.K only if SpecificElementExtensions)
Sub-total_________________________3 (4 when isString(element) is true).

Calling $(document) then $(document), there will be many function calls the first time, and never any fewer than three calls the second time.

Element.extend = (function() {
  if (Prototype.BrowserFeatures.SpecificElementExtensions)
    return Prototype.K;

  var Methods = { }, ByTag = Element.Methods.ByTag;
  Object.extend(function(element) { 
      if (!element || element._extendedByPrototype || 
          element.nodeType != 1 || element == window) return element; 
      var methods = Object.clone(Methods), 
        tagName = element.tagName, property, value; 
      // extend methods for specific tags 
      if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]); 
      for (property in methods) { 
        value = methods[property]; 
        if (Object.isFunction(value) && !(property in element)) 
          element[property] = value.methodize(); 
      } 
       element._extendedByPrototype = Prototype.emptyFunction; 
      return element; 
  }, {
    refresh: function() {
      // extend methods for all tags (Safari doesn't need this)
      if (!Prototype.BrowserFeatures.ElementExtensions) {
        Object.extend(Methods, Element.Methods);
        Object.extend(Methods, Element.Methods.Simulated);
      }
    }

  extend.refresh();
  return extend;
})();

Element.extend, first call

However, during the first call to $(document), there is a check to Prototype.BrowserFeatures.SpecificElementExtensions. This is done because when SpecificElementExtensions is true, then the Library attempts to add the properties to DOM interface prototypes, e.g. HTMLHeadingElement.prototype. This happens elsewhere in the code, only when a __proto__ property returns a truthy value on created elements. If this feature is supported, the author has makes modifications to the DOM object interfaces, e.g. "HTML" + element.tagName + "Element", et c, but only when the userAgent does not match /Apple.*Mobile.*Safari/. (He makes said modifications elsewhere in the code, Element.addMethods -> findDOMClass).

Element.extend's closure calls Object.extend (needs review) to add the refresh() method to element.extend (rather than perform a simple assignment). The closure then calls extend.refresh() before returning the method extend, which gets assigned to Element.extend.

+ Element.extend's closure -> Object.extend -> extend.refresh -> Object.extend...3
                                      Object.extend...1
subtotal:_____________________________________________9

calls: 5, Depth: 3 These calls are done when the file is loaded. They don't affect performance of $.

After extend has been assigned to Element.extend, it is invoked. Element.extend calls Object.clone, which calls Object.extend, then ByTag, Object.extend, n calls to isFunction, n calls to methodize, where n is the number of properties of methods. The element is then "methodized", which adds Ajax, et c to a FORM element.

$ -> isString..........................................(2)
     document.getElementById...........................(+1) (when isString(element) is true)
     Element.extend ->  Object.clone -> Object.extend..(2)
                        ByTag -> Object.extend.........(2) (depends on tagName).
                        isFunction.....................(n = Size(methods) = 64 + 8)
                        methodize......................(n = Size(methods) = 64 + 8)
Total:_________________________________________________151

Calling $("sp-searchtext") results in 151 function calls the first time it is called. Non-form elements will have 133 calls the first time.

This happens for every object that is got by dollar function for browsers that don't support modifying DOM prototypes.

It considerably more complex and inefficient the first time around, when SpecificElementExtensions is false:

Object.extend(Methods, Element.Methods);
Object.extend(Methods, Element.Methods.Simulated);

Where is $ Used Internally?

All of the Element.methods of PrototypeJS functions (the ones that got "methodized" in dollar, use the dollar function, as well, adding extra overhead of a function call.

This allows each of Element.methods to be used as a static method. This also adds considerable extra cost, though, to each call.

Element.Methods = {
// (GS) visibility is not display!
  visible: function(element) {
// (GS) style properties do not reflect element state.
// The value 'inherit' is not considered.
    return $(element).style.display != 'none';
  },

Element.Methods.visible does not deal with CSS visibility, but instead returns the style.display != 'none'. The display property does not reflect the currentStyle or computed style. Also, the CSS display property can have the value inherit.

Other Element.Methods functions that use the dollar function are more expensive. For example:

// (GS) Avoid this long chains of calls. Hard to debug.
// (GS) calling Element.extend on each element is expensive, 
// up to 1600+ function calls for a FORM with 10 elements in IE.

  descendants: function(element) {
    return $A($(element).getElementsByTagName('*')).each(Element.extend);
  },

...

var Enumerable = {
  each: function(iterator, context) {
    var index = 0;
    iterator = iterator.bind(context);
    try {
      this._each(function(value) {
        iterator(value, index++);
      });
    } catch (e) {
      if (e != $break) throw e;
    }
    return this;
  },

...


Object.extend(Array.prototype, {
  _each: function(iterator) {
    for (var i = 0, length = this.length; i < length; i++)
      iterator(this[i]);
  },

Considering a FORM with 10 input elements and nothing else, there will be:

Element.descendants...................1
    $A-->Array........................2
     +--$.............................151
        +--getElementsByTagName('*')..1
     each.............................1 x 10 (ten INPUT elements)
      +--bind.........................1 x 10 
      +--_each........................151 x 10 (151 methods, ten INPUT elements)
TOTAL.................................1685

One thousand, six hundred, and eighty-five function calls.

The call to isFunction in Element.extend might be something that could be refactored:

value = methods[property];
Object.isFunction(value)

Variable methods is a collection of functions. Calling Object.isFunction(value) should always return true here and this is something that the author can have control over because he owns methods (Law of Demeter).

Always Use Dollar

When using PrototypeJS, it is uncertain what properties will be present on an Element. This is because PrototypeJS may have already modified that element or its associated prototype. This dilemma of ambiguity can be avoided by the PrototypeJS user always using the dollar function and depending on PrototypeJS.

$("adiv").parentNode.show(), will have unexpected results, and will result in error in IE, if the parentNode has not been previously modified via Element.extend()

$($('adiv').parentNode), will result in no less than seven function calls, and that is only after $('adiv') has been called and $($('adiv').parentNode) has been called.

More Modifications to Host Objects

I'm going to back up a little bit and look at details I skipped over, the modification Host objects.

LoD Recap

If the implementation's internal code undergoes change, or if other browsers provide similar properties with slightly different implementation then the code that relies on assumptions of implementation-specific details based on those properties (col.__proto__) will fail. Implementations have been known to change, in this regard (bug 390411)

function findDOMClass(tagName) {
    var klass;
    var trans = {
      "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
// (GS) et c...    
    };

    if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';

// (GS) Does not provide any feature detection about the object.
    if (window[klass]) return window[klass];
    klass = 'HTML' + tagName + 'Element';
    if (window[klass]) return window[klass];
    klass = 'HTML' + tagName.capitalize() + 'Element';
    if (window[klass]) return window[klass];

    window[klass] = { };

// (GS) Does not provide any feature detection about the object.
    window[klass].prototype = document.createElement(tagName).__proto__;
    return window[klass];
  }

  if (F.ElementExtensions) {
    copy(Element.Methods, HTMLElement.prototype);
    copy(Element.Methods.Simulated, HTMLElement.prototype, true);
  }

  if (F.SpecificElementExtensions) {
    for (var tag in Element.Methods.ByTag) {
      var klass = findDOMClass(tag);
      if (Object.isUndefined(klass)) continue;
      copy(T[tag], klass.prototype);
    }
  }

The findDOMClass assumes that the window will have an object based on tagName or that if this is not the case, then the element's __proto__ property will be readable.

There is no guarantee by any standard that there will be a __proto__ property present or what value it will hold, if any.

There is no guarantee by any standard of an HTMLTableRowElement on window, nor any guarantee of what modifying its prototype will have. The library makes broad assumptions and performs no capability tests.

Replace the Host Environment's Element?

Not satisfied by altering over certain Host environments' Element with its own properties, PrototypeJS seeks to create a constructor to replace those environments' Element with its own, copying over all enumerable properties from the Host's Element. In Firefox, this includes all of QueryInterface.

This creates the dilemma of having a debilitated Element. Where previously, in Firefox, Element was native code, and Element.prototype.TEXT_NODE had the value 3, now, Element is a PrototypeJS constructor function and Element.prototype.TEXT_NODE is undefined.

(function() {
  var element = this.Element;
  this.Element = function(tagName, attributes) {
    attributes = attributes || { };
    tagName = tagName.toLowerCase();
    var cache = Element.cache;
    if (Prototype.Browser.IE && attributes.name) {
      tagName = '<' + tagName + ' name="' + attributes.name + '">';
      delete attributes.name;
      return Element.writeAttribute(document.createElement(tagName), attributes);
    }
    if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
    return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
  };
  Object.extend(this.Element, element || { });
}).call(window);

Element.cache = { };

Event.extend - Don't do It!

The same approach is used here

Event.extend = (function() {
  var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
    m[name] = Event.Methods[name].methodize();
    return m;
  });

  if (Prototype.Browser.IE) {
    Object.extend(methods, {
      stopPropagation: function() { this.cancelBubble = true },
      preventDefault:  function() { this.returnValue = false },
      inspect: function() { return "[object Event]" }
    });

    return function(event) {
      if (!event) return false;
      if (event._extendedByPrototype) return event;

      event._extendedByPrototype = Prototype.emptyFunction;
      var pointer = Event.pointer(event);
      Object.extend(event, {
        target: event.srcElement,
        relatedTarget: Event.relatedTarget(event),
        pageX:  pointer.x,
        pageY:  pointer.y
      });
      return Object.extend(event, methods);
    };

  } else {
  // (GS) Event.prototype is not guaranteed to be available in 
  // any browser.
    Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__;
    Object.extend(Event.prototype, methods);
    return Prototype.K;
  }
})();

Object.extend - Doesn't Account for JScript DontEnum Bug

Object.extend = function(destination, source) {
  for (var property in source)
// (GS) Does not account for JScript DontEnum bug.
    destination[property] = source[property];
  return destination;
};

In IE, the keys of objects are skipped without properly checking the DontEnum flag. The skipped properties include the useful and often overridden toString and valueOf. Special and careful considerations should be made to address this problem.

$A - Don't Rely on The Return Value of An Object's toString

if (Prototype.Browser.WebKit) {
  $A = function(iterable) {
    if (!iterable) return [];
// (GS) Do not on the return value of an object's toString.
    if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
        iterable.toArray) return iterable.toArray();
    var length = iterable.length || 0, results = new Array(length);
    while (length--) results[length] = iterable[length];
    return results;
  };
}

A toString() with the return value of "[object NodeList]" implies nothing else about the object it was called on.

The readAttribute function, branched for Internet Explorer.

Example

Example page: http://www.apple.com/itunes/

javascript:alert($(document.body).readAttribute("onload"))

Will result in partial string representation of a serialized function. The string begins with ")".

_getEv: function(element, attribute) {
// (GS) Do not rely on function decompilation.
  var attribute = element.getAttribute(attribute);
// (GS) Do not prevent yourself from renaming a function.
// (GS) Broken - slice starts at wrong position.
  return attribute ? attribute.toString().slice(23, -2) : null;
},

Function readAttribute calls getAttribute on the element. In Internet Explorer, calling getAttribute always reflects the property with the same name. In this case the property has the value of the function object, loadShortcuts.

It isn't clear why the substring of 23 should be taken from the function's source code. The approach of relying on the toString of an object defined elsewhere in the code makes it hard to rename that function.

Ajax.Request - Add an Underscore?

  request: function(url) {
    this.url = url;
    this.method = this.options.method;
    var params = Object.clone(this.options.parameters);

    if (!['get', 'post'].include(this.method)) {
      // simulate other verbs over post
      params['_method'] = this.method;
      this.method = 'post';
    }

    this.parameters = params;

    if (params = Object.toQueryString(params)) {
      // when GET, append parameters to URL
      if (this.method == 'get')
        this.url += (this.url.include('?') ? '&' : '?') + params;

// (GS) All browsers support properly-formatted URIs.
// (GS) Do not add extra "_" parameter for some browsers.
      else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
        params += '&_=';
    }

Broken Unescaping of HTML Strings

PrototypeJS adds escapeHTML and unescapeHTML to String.prototype. The problem with this code was discussed quite some time ago on comp.lang.javascript.

if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
  escapeHTML: function() {
    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
  },
  unescapeHTML: function() {
    return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
  }
});

// (GS) Do not use with for simple property assignment
with (String.prototype.escapeHTML) div.appendChild(text);

The problem is that the escape character for entities is &. This will have the result in browsers identifying as Webkit or MSIE of:-

    "&amp;lt;".unescapeHTML() to "<" 

The with statement is not needed to provide a more compact approach:

"".escapeHTML.div.appendChild("".escapeHTML.text);

Enumerable.include - Two Loops? Strict Equality, or Loose Equality?

Enumerable.include. This function returns true if the Enumerable contains the object.

  include: function(object) {

    // (GS) Call the indexOf method on the object.
    if (Object.isFunction(this.indexOf))
      if (this.indexOf(object) != -1) return true;
    // (GS) If not found, search again.
    var found = false;
    this.each(function(value) {
    // (GS) Should use strict equality, === 
      if (value == object) {
        found = true;
        throw $break;
      }
    });
    return found;
  },

Enumerable.include first calls this.indexOf(), and if that returns false, the collection is searched using a similar algorithm to Array.prototype.indexOf, except not using strict equality. This tactic results in the collection being looped over twice. Function each calls a function for each iteration. If non-strict equality is desired, then the strict equality check that can result by calling this.indexOf should be omitted.

A plausible solution would be return this.indexOf(object), and include a strict === check if indexOf were not a function.

getDimensions - clientWidth != offsetWidth, And Don't Expect clientWidth to be non-zero in IE

  getDimensions: function(element) {
    element = $(element);
    var display = $(element).getStyle('display');
    if (display != 'none' && display != null) // Safari bug
      return {width: element.offsetWidth, height: element.offsetHeight};

    // All *Width and *Height properties give 0 on elements with display none,
    // so enable the element temporarily
    var els = element.style;
    var originalVisibility = els.visibility;
    var originalPosition = els.position;
    var originalDisplay = els.display;
    els.visibility = 'hidden';
    els.position = 'absolute';
    els.display = 'block';
    var originalWidth = element.clientWidth;
    var originalHeight = element.clientHeight;
    els.display = originalDisplay;
    els.position = originalPosition;
    els.visibility = originalVisibility;
// (GS) use offsetWidth/Height.
    return {width: originalWidth, height: originalHeight};
  },

This method has the unique quality of being one of the only methods in PrototypeJS to have a code comment.

The code tries to get the offsetWidth and offsetHeight of the Element. If the element is not displayed, then these properties will be 0, and in that case, the function will display the element temporarily, then calculate its clientWidth - not the offsetWidth before immediately hiding it. This function can be expected to provide inconsistent results when the element's CSS display is changing. What's worse: Unless the element has a CSS width, the clientWidth will be 0 in IE.

Calculating Offsets

Finding an Element's position is hard.

Calculating the position of an element requires adding the offsetTop/Left of offsetParents, border offsets, and scroll offsets of parentNodes.

A BODY with margin, position and/or border can affect the offsetTop/Left differently, depending on the browser. This is a harmful effect propagated by the CSSOM Views standard.

cumulativeOffset

PrototypeJS misses both points and just adds offsetTop/Left.

  cumulativeOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
    } while (element);
    // (GS) Don't call a function that returns an 
    // Array with top and left properties.
    // Better just return an Array or an Object.
    return Element._returnOffset(valueL, valueT);
  },

The seriousness of the problem is apparent when precision is crucial and off by a pixel or more would be failure, (e.g. make element to overlap another element exactly, activate a drop zone).

The bug also exists in positionedOffset and affects every function that uses either function, including within, scrollTo, and all the functions that call those functions, such as others found in Scriptaculous (dragdrop.js, effects.js).

Conclusion

A few core parts of PrototypeJS exhibiting some serious bugs. There are many more issues, such as Hash, but this entry is already getting excessively long.

PrototypeJS is designed in a non-standard way around the modification of host objects. The code that exists is complicated and not very efficient. I cannot recommend using this library for anything.

Posted by default at 1:37 AM in JavaScript

JavaScript Trends Friday, 30 May 2008

I came across a blog post yesterday that had a lot of misinformation. The post had received several comments and trackbacks thanking and commending the author for his "helpful" post. At first I was mad. "How can these people give this guy credit for misinformation," I thought. I wrote up a comment correcting on various points, intending to publish it on the author's blog.

Thankfully, my comment could not be posted due to some problem with the weblog. I am thankful of this because I ended up thinking about the problem on a greater depth.

I decided to respond here, providing a review below. I was more bothered by the phenomenon of people eager to learn misinformation. The more I thought about that phenomenon, the more I realized that it's not the author's fault, it's just the way things are. I discovered a part of the web that I want to change.

The expert, in this case, is Alex Russell, of Dojo fame, as Ajaxian likes to call it. It doesn't really matter who it is. In fact, it could have been me several years ago, when my JavaScript knowlegde was not as strong. I have written some really awful javascript that fortunately did not become famous.

My initial technical responses to the blog entry, which I have cut and snipped, are interspersed below. I have done my best to not take the author out of context and provide clear, relevant feedback. My real "response" starts below the proceeding technical response.

Technical Response

alex:

Everything in JavaScript is an Object. Even functions

Not true.

Fact: There are primitive values, too: undefined, null, true, 3, and "foo" are all primitive values. Not objects.

alex:

Every object is always mutable

Fact: An EcmaScript object itself is always mutable, but property-setting will not be always successful and error-free.

  1. Host objects do not need to implement setters for each property. For purpose of providing a relevant example to back up my claim:

    function fixEvent(e) {
      e=e||event;
      e.pageX = 1; // getEventPageX(e);
    }

    - will cause an error in Firefox. The pageX property actually needs a patch, because creating events, the pageX property doesn't get set correctly (bug 411031). So the argument "every object is always mutable" leads to developers doing things like writing a "fixEvent" function. It is not safe to do so.

  2. Some properties are tagged ReadOnly. A String or Function object's length property, for example.
alex:

The dot operator is equivalent to de-referencing by hash (e.g., foo.bar === foo["bar"])

Fact: The two property access operators .<Identifier> and [<identifier-string>] perform identical operations.

alex:

The new keyword creates an object that class constructors run inside of, thereby imprinting them

False.

Fact: There are no class constructors. No classes in the current release of EcmaScript 262 r3.

The new operator (an operator) creates a new object in context of the function on which its called. Nothing gets imprinted.

alex:

Functions are always closures (combine w/ previous rule to create OOP)

If used very carefully, closures can be used to mimic some of the constructs found in OO languages. However, to say that closures "create OOP" is false and misleading.

alex:

The this keyword is relative to the execution context, not the declaration context

There is no "declaration context". You seem to have made this up as a way to describe the way you think JavaScript works.

alex:

The prototype property is mutable

Not informative.

Fact: A property is a reference.

If the property's value is a native EcmaScript object, then it will be mutable. It goes without saying that this is true even if the name of the property is prototype.

Considering a prototype property of a function, where the prototype's value is an EcmaScript object: {}, then it is mutable (as discussed above).

alex:

Jeremy: great clarifications. Thanks.

Jeremey provided a false statement with code that was confusing. Jeremey wrote:

y = new Foo();
assert(y.gimme() == 2);

But:

button.onclick = "alert(y.gimme())" will error rather than alerting "2", because in an event handler, "this" refers to the elm which sourced the event.

Fact: This is a perfectly valid assignment of a string value to an onclick property of a button object.

Jeremy's example of a button object with an onclick property assigned to a string value:-

button.onclick = "alert(y.gimme())"

- the string "alert(y.gimme())" is not eval'd.

(Continuing Jeremy's post)

because in an event handler, "this" refers to the elm which sourced the event.

Has relevant meaning in a script:-

// assign function to onclick.
button.onclick = y.gimme;

Definitely not in:

<body>
<script>

function Foo() {
this.x = 1;

this.gimme = function() {
return this.x + 1;
}
}

y = new Foo();
</script>

<button onclick="alert(y.gimme())">click me</button>

</body>
Result

When the button is clicked, the method gimme is called with y as the thisArg. The number is returned and displayed as a string in the alert box.

Thanking Jeremy for Jeremy's mistake and calling the mistake a clarification does not make Jeremy correct. It is not helpful to Jeremy or anyone else.

alex: (To Dethe):

My statement about objects (variables whose .constructor property in some way descends from Object) was correct.

Fact: No, it was not correct. Now you've made another misstatement.

Fact: (new function(){}) - is an object, not a variable. Its - constructor - property doesn't "descend" from Object, either.

Fact: var i = 0, len; - is an example of two variables that do not have a .constructor property.

Fact: The term "descend" has no meaning in context of describing a constructor property; it is fictitious terminology to describe the way you imagine JavaScript works.

(alex: To Dethe, continued):

Also, the hash deference is exactly equivalent. That there's no way to have a JS lexer handle an variable name with spaces in it in no way detracts from the equivalence, it just means that the dot operator has to follow the rules of thing that aren't string literals.

Fact: There is no "dot operator."

Fact: The fullstop, "." also has meaning in numbers, for example, 4.2.toString().

Fact: There are two property access operators: "." and "[]". There are many "things that are not string literals." The "." property access operator can be used only for valid identifiers.

I think you were using the term "de-reference" to try to describe getting a value. Now your using "deference". I'm not sure what to make of that.

The Real "Response"

My real "response" in the larger sense, is that I'm taking a stance. I'm going to try and change the web.

Think 2.0

A who's-who in web 2.0 is destructive to the web, in a way. The idea should win, not the individual. The popular libraries have spread ideas for web development across the web but they have also played a big part in the "whos-who" trend that I see.

What I see demonstrated in the blog entry that I replied to is a misunderstaning of JavaScript that received positive acknowledgement and review. The question is: Why? Is it because the entry is simple and clear?

Ask Why

If a "famed" individual can be commended for teaching JavaScript facts that are false and inaccurate, what does that say about what web developers value in the web? Where are we headed? I am hoping that this trend will reverse itself. The reversal of this trend starts by questioning things. I question things and you should, too.

Angry, Bitter, and Vile?

As much as I've pointed out bad parts of Dojo and Google (And jQuery and PrototypeJS and YUI), I probably sound like a bitter, angry person. In fact, there are people who would love to have you believe that I am nothing more than that. I've made my observations and shared them, even at the expense of sounding mean and bitter. I want the web to change, and in my next entry, I'll clearly explain the direction I want the web to turn.

Posted by default at 1:53 PM in Uncategorized

Factory Aspect, Added to a Decorator: APE.getById Sunday, 11 May 2008

This article describes the generic concepts for creating and mixing design patterns. The basic principle for all design patterns is: Encapsulate the parts that vary.

The problem is finding a way to create a generic Factory that can be reused on various constructor functions for element Decorators. This article explains the problem and the process for finding the solution.

Decorator Factory Aspect

A Decorator Factory Aspect is a Factory method, added as an Aspect to a constructor of a Decorator.

Before I explain how to add a Factory to a constructor function for an element decorator, I should first define Decorator (also called a wrapper), Factory and Aspect.

Decorator Pattern
makes it possible to extend (decorate) the functionality of a class by adding a new decorator class that wraps the original class. (Wikipedia link)
Factory Pattern
The Factory pattern is a creational design pattern that encapsulates the processes of creating objects (Wikipedia link)
Aspect
introduces separation of concerns, specifically cross-cutting concerns, as an advance in modularization (Wikipedia link)

Decorator Examples

Decorator is very common in JavaScript. For example: YAHOO.util.Element decorates an element, jQuery decorates an array of elements.

Factory Example

The Factory gets or creates a decorated element. The id of the wrapper is the same as the id of the element. This is the part I want to make reusable:

/**
 * @constructor
 * @param {String} id - the id of the element and widget. 
 */
function ElementWrapper(id, x) {
  this.id = id;
  this.x = x;
}

// Factory. 
// TODO: How can I make this generic/reusable?
ElementWrapper.instances = {};
ElementWrapper.getById = function(id, x) {
  if(this.instances.hasOwnProperty(id)) return this.instances[id];
  return this.instances[id] = new this(id, x);
};

ElementWrapper.prototype = { 
  show : function() { 
    document.getElementById(this.id).style.visibility = "visible";
  }
};
Benefits

Solves the problem of creating only one decorator per element id.

By calling getElementById, the decorator can avoid some of the problems with changing node references with innerHTML (though state changes must still be managed manually).

Problem: DRY

Don't Repeat Yourself

Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.

It is cumbersome and error-prone to write out a Factory each time. Since this is an idiom I use a lot, it makes sense to make it reusable.

I want to have a generic getById method that can be reused and will return an instance of the constructor that it is called on. I want to be able to pass extra arguments to that constructor (varargs).

Encapsulate the Parts That Vary

What varies?

The id parameter variable of getById does not change; it will always be present in any generic Factory. The parts of the Factory that vary are: The additional zero or more arguments (varargs, this case, x), and the context, or thisArg.

Resolving the context arg is easy.

If I can solve passing varargs to a constructor in a generic context, it will be possible to create a generic Factory Aspect.

Function newApply

A way to call new with variable arguments would solve this problem. A new + apply() would provide the varargs functionality of apply, but passed to [[Construct]], not [[Call]].

This has been solved in APE core. The source code for APE.newApply:


/** 
 * @param {Function} fun constructor to be invoked.
 * @param {Array} args arguments to pass to the constructor.
 * Instantiates a constructor and uses apply().
 */
newApply : function(fun, args) {
    if(arguments.length === 0) return;
    var f = arguments.callee, i;

    f.prototype = fun.prototype; // Add prototype.
    f.prototype.constructor = fun;

    i = new f;
    fun.apply(i, args);  // Apply the original constructor.
    return i;
}

What's it Good For?

Now I can create the generic getById function I wanted. This function can be added as an aspect to any constructor function. Factory Aspect APE.getById is a part of APE core.

getById : function(id) {
    if(!this.hasOwnProperty("instances")) this.instances = {};
    return this.instances[id] || (this.instances[id] = APE.newApply(this, arguments));       
},

Using the Generic getById

This getById method can be used with ElementWrapper (above) or any other constructor that acts as a Decorator to an element and accepts the element's id as its first argument.

Slider = function(id, dir) { /* ... */ };

// Factory. 
Slider.getById = APE.getById;

Then I can use:

Slider.getById( "weight", 1 );

Subsequent calls to:

Slider.getById( "weight" );

— will return the same Slider instance.

More Examples

I have used this approach for many parts of APE, including Calendar, Draggable, and Slider. It is most useful for building widgets.

Reusable Concept

Another closely related technique is Decorator that accepts an element instead of an element's id. This is covered by APE.getByNode.

Source Code

APE.js

Reflection

In most patterns, encapsulating the parts that vary entails creating an class. However, in JavaScript, this particular pattern was simple to implement by using just two functions (APE.getById and APE.newApply) and leveraging the dynamic nature of JavaScript.

Forward to ES4

ES4 has had some proposals for something called a splat operator.

fun(...argsOrArray); 
new fun(...argsOrArray);

This proposal will allow passing varargs to a constructor or a function call.

It is unclear how the splat operator, if accepted into the language, will work with functions which expect typed arguments, in strict mode.

Links

Orthogonality and the DRY Principle, A Conversation with Andy Hunt and Dave Thomas, Part II by Bill Venners March 10, 2003

Posted by default at 10:35 PM in JavaScript

APE JavaScript Library Monday, 5 May 2008

Frustrated with every other JavaScript framework, I have decided to write something better.

Points

  • Minimal Framework, mainly used for AOP and OOP functionality
  • All code is tested using YUI Test (TDD)
  • AOP Event System with asynchronous error handling
  • Namespacing
  • Modularity, Cohesion, Packaging
  • No browser detection
  • JSDoc

Here it is: APE JavaScript Library

Testing

By using a test driven approach, I was able to avoid problems that I found in other libraries.

APE Core

The core of APE is so small that the overhead of adding APE to a project is significantly less than that of other libraries.

I generally don't like a lot of the libraries that use a "GOD" object approach. These objects can be recognized by being undefinable by more than a few words. The approach is quite popular and annoys me to no end. How do you describe the dojo object, for example?

The APE object itself is defined in APE.js, providing only the most basic things that a framework would need: 1) Object creational features, and 2) a core namespace. Although some might argue that a library core needs more than that.

The APE object is only 3k minified and less than 1k gzipped. The size is not the point. The point is that APE is not a kitchen-sink, or God object.

Help Wanted

You can join, too! Here's what APE needs:

New Features

There is much more that can be added. For example, some ADT's: Resizable (croppers, windows), FormSerializer.

SVN and Bugzilla

I still need to add hosted SVN to the directories and install bugzilla.

Performance Tests

I've always been big on performance, and not just for JavaScript but all areas of life, and I am very competitive in nature.

APE needs benchmarks to compare with other libraries. This will provide useful analysis for other library authors who aspire to have code that is as performant as that in APE.

Anyone interested in joining this project contact dhtmlkitchen - at - gmail - dot - com.

Posted by default at 7:26 PM in Uncategorized

Testing JavaScript Saturday, 19 April 2008

Slides from JS Meetup's "Testing JavaScript"

Posted by default at 1:03 AM in JavaScript

Load Time Constants, Part I Thursday, 21 February 2008

One issue that comes up when writing scripts is addressing upper and lower case tag names.

Problem

An XHTML document served with the content type application/xhtml+xml will have all lower case tag names in the DOM. The exact same page served with the content type text/html will have all upper case tag names.

This can affect the way we script our pages by requiring us to clutter up the script with calls to toLowerCase().

Example

function getCoords(el) {
  for( var parent = el.parentNode; parent && parent !== container; 
                                         parent = parent.parentNode) { 
    if( parent.tagName.toLowerCase() == "table" ) {
      var pcs = getComputedStyle(parent, "");
    }
  }
}

It's easy enough to call el.tagName.toLowerCase() but when this is done repeatedly in a loop, it can slow things down.

Solution

One way to avoid that issue is to define a run time constant. This would be done in a closure over the function that was calling toLowerCase()

(function(){ 

var TABLE = /^h/.test(document.documentElement.tagName) ? "table" : "TABLE";

function getCoords(el) {
  for( var parent = el.parentNode; parent && parent !== container; 
                                         parent = parent.parentNode) { 
    if( parent.tagName == TABLE ) {
      var pcs = getComputedStyle(parent, "");
    }
  }
}
})();

Scope

Function getCoords() can see TABLE, but nothing outside of that closure can see getCoords.

Function getCoords must then be exported by assigning it to a property of a globally accessible object (using the Module Pattern or Exporting Pattern).

Conclusion

Having a constant can help to speed up the loop by avoiding the property lookup and an extra function call to toLowerCase().

Having one constant value reduces the number of strings being created and garbage collected. Since an active garbage collector can also hurt performance, a constant value avoids this hit.

Finally, the result can be compressed using a compression tool, resulting in TABLE being converted in to a one letter symbol.

Posted by default at 5:20 PM in JavaScript

Event Notification System Friday, 4 January 2008

An Event Notification System is an object that manages notification of events to multiple callbacks. The Event Notification System uses an Event Registry to store the callbacks as bound methods. When the event fires, the callbacks are invoked.

Event Registry

An Event Registry is a store of bound methods. An Event Registry is used by an Event Notification System. The Event Notification System is tightly coupled with the Event Registry. Sometimes it is referred to as the Registry. In reality, the Registry is just a data structure and the Event Notification System is a behavioral object.

Almost Every JavaScript library has an Event Registry, or at least some way of dealing with event notification.

For example:

// YUI:
YAHOO.util.Event.addListener( link, "click", linkClickHandler, thisArg );

// Prototype: (not a registry, but the old 'addEvent' function renamed).
Event.observe( link, "click", linkClickHandler ); 

// Dojo:
dojo.connect( link, "onclick", window, "linkClickHandler" );

They're all different in how they work.

The Event Registry is useful for a few reasons.

  • It allows multiple callbacks to be assigned to a function call.
  • Provides a usable alternative to attachEvent. Internet Explorer 7 and below has attachEvent/detachEvent. The callback function for attachEvent executes in global context (this is window), not the object it was attached to.

A good Event Registry solves these problems. A good Event Registry also allows for context resolution with an optional thisArg. A good Event Registry also allows custom events to be registered using the same interface.

A poorly designed Event Registry concerns itself with things related to native events (DOMContentLoaded, keyPress, et c). A poorly designed Event Registry does not pass an event object to the callback (perhaps trying to use eval to pass varargs).

Error Handling in an Event Notification System

Callback Errors Should not Break the Registry

A good Event Registry does not allow any callback to break the registry.

One common problem in most Event Notification Systems (such as Dojo, Mochikit, YUI, and jQuery) is that they allow the callback to break the System. If a callback fails, it prevents subsequent callbacks from firing. A callback should not be given the ability to break the Registry.

Here's how to break a Registry that doesn't consider errors:

var passed = false;
addCallback( link, "click", function(){ setTimeout(checkTitle, 500); } );
addCallback( link, "click", function(){ throw Error('bad'); } );
addCallback( link, "click", function(){ passed = true; } );

function checkTitle(){ 
    if(!passed) 
        alert("registry broken: second callback did not fire.");
    else 
        alert('passed');
}

Callbacks sometimes throw Errors. It is important for the Event Registry to consider this and take the responsibility to handle these errors properly. If an error occurs in a callback, it should not break the Registry.

It should be guaranteed that all callbacks fire, even when earlier callbacks throw errors. This is a natural expectation; it's exactly how DOM Events work:

DOM Events Test

(function(){
var s = document.getElementById('r-test');

var el = document.getElementById("registry-dom-event-button");
if(!window.hasDocumentListeners) {
    el.addEventListener( "click", setUpCheck, false );
    el.addEventListener("click", throwError, false );
    // setTitle must fire.
    el.addEventListener( "click", setTitle, false ); 

    window.hasDocumentListeners = true;
}
function setUpCheck(){ setTimeout(checkTitle, 500); }
function throwError(){ document.title = ""; throw Error('bad'); }
function setTitle(){ document.title += 'ok'; }
function checkTitle(ev) {
    if(document.title != "ok") {
        alert("DOM Events broken: setTitle did not fire. " + document.title );
    }
    else {
        alert("passed");
    }
}
})();

Result and Analysis

There should be 1 error and an alert passed. This indicates that after the error happened, the setTitle callback successfully fired.

This example assumes:

  • Callbacks fire in the order in which they were registered.
  • The bad error in the first callback does not stop subsequent callbacks from firing.
  • The button supports the EventTarget interface (not in IE7).
  • The button supports onfocus (not in safari bug 16331).

Proper Callback Error-Handling

Throwing the error in a separate thread allows the callstack to continue without breaking. Any errors that are thrown are thrown in the correct order in the callstack. The Event Publisher's fire function would have something like this:

try {
// If an error occurs, continue the event fire,
// but still throw the error.
  callback.call( thisArg, ev );
}
catch( ex ) {
  setTimeout("throw ex;", 1); 
}

The one subtle issue is that setTimeout uses global scope, like the Function constructor, not like eval, which runs in the calling context's scope.

A closure must be used to preserve the ex variable.

try {
// If an error occurs, continue the event fire,
// but still throw the error.
  callback.call( thisArg, ev );
}
catch( ex ) {
  setTimeout(function(){ throw ex; }, 1); 
}

Event Registry Test

The remaining problem with the above code is that the error condition is untestable. Writing a test suite forced me to realize this and I changed the design.

try {
if(csi[0].call(csi[1], e) == false)
  preventDefault = true; // continue main callstack and return false afterwards.
}
catch(ex) {
  APE.deferError(ex);
}

Where APE.deferError is defined:

deferError : function(error) {		
  setTimeout(function deferError(){throw error;},1);
}

I have included the source code for my own Event Registry, along with this test, which shows how I managed to test APE.deferError.

Performance?

Wrapping each callback call in a try catch might seem to be bad for performance. I tried it with mousemove event on my drag code, dragging multiple drag objects at a time (example), and it seemed fast enough; I did not notice performance problems in any browser. There is most likely some performance overhead using this approach, but I did not find a need to write a benchmark.

src should never be a string. Although this may seem obvious, YUI actually allows src to be a string, where the string represents an element's ID. The document is polled regulary until the element with the id matching string is found and then the callback is attached to that element. If the element has been renamed, the document is still polled and silent failure occurs.

This can lead to silent failure or corrupted application state if the element is not found. It is not recommended.

Packaging and API Design

The Event Notification System is a low level component with no external dependencies.

Being a low level component, the Event Notification System should be maximally stable (no efferent couplings), and maximally abstract. In this case, the Event Notification system is maximally abstract because it can't be subclassed or used independently.

Stable Dependencies Principle

Depend in the direction of stability

Stable Abstractions Principle

A package should be as abstract as it is stable.

Reuse Equivalence Principle

The Granule of Reuse is the Granule of Release.

The Event Notification System is a low level component with no external dependencies. It is intentionally packaged as a single, tested unit. It amplifies the essential (event notification) and eliminates the irrelevant.

Creating special cases for handling DOM events (keyCode, et c), would reduce abstraction. These special cases are perfectly valid, but do not belong in the Registry. Special case needs can either be hard-coded into end-implementation code (using feature/capability detection) or, if the special-case logic is complex, programmed into an object that performs a task (such as an Adapter object).

An example of an Adapter object would be a Content Load Adapter or a KeyEvent Adapter (key events are highly inconsistent across platforms). Such objects would be slightly higher-level and, having at least one dependency, would be less stable (though this is not a bad thing).

Department Store JavaScript

[insert_popular_library_name_here] usually include more code than any one application could possibly use in an attempt to cover the needs of every application.

Libraries that add more functionality into one module than is usually needed, or create modules that are not cohesive do so in spite of commonly known software package design concepts. The one-stop library approach is appealing because it allows developers to "stop cobbling bits of javascript."

Performance (Again)

Load Time Performance problems can be acheived by creating custom javascript builds on the server. Hand-rolled "combination" files or utils files are fine for web sites with fewer pages. Sites that don't require 200k+ of additional javascript should not include such functionality.

Posted by default at 9:42 PM in Uncategorized

Opera Bug: getComputedStyle Returns Margin for Unset Top/Left Values Thursday, 8 November 2007

In Opera 9.2 getComputedStyle(el, '').getPropertyValue returns the margin value for top/left values when the top/left values aren't set. In Safari the returned values are 'auto' in this case.

Example

testcase showing bug in Opera and Safari

Workaround

The way to avoid the problem is to explicitly add top and left values to the stylesheet:

#Test {
  top: 0;
  left: 0;
}

Then getComputedStyle will return correct values for top/left (0px) in Opera 9 and Safari.

pixelXXX

A convenient alternative would be a currentStyle.pixelLeft. Only Opera and IE support currentStyle and only Opera supports currentStyle.pixelLeft. (IE supports style.pixelLeft; this only reads from the style attribute)

Mozilla does not support pixelXXX properties at all, though Opera, IE, and Safari 3 all do.

Posted by default at 6:25 PM in Browsers

Iteration, Enumeration, Primitives, and Objects Sunday, 21 October 2007

Iteration VS Enumeration

When trying to understand a language, it is necessary to first understand the words.

iteration
the for statement (§12.6.3)
enumeration
the for in statement (§12.6.4)

While writing Enumeration and Object Oriented JavaScript, I read and reasearched (I also grew a beard and lost about 20lbs in the process). I read blogs and libraries to see what other programmers were doing. I came across a peculiar quote by Alex Russel (dojo fame).

The contortions that Ajax toolkit vendors go through to keep iteration over JavaScript objects and primitives coherent is, quite simply, insane. Much of Dojo, in particular, is designed around this problem.

—Alex Russel

Iterate Over a Primitive?

That sounds completely insane! I read through dojo.js (uncompressed), which was pretty heavily commented, but could find no such contortion.

I thought about this some more, and I'm pretty sure it's not possible.

Attempting to enumerate over a primitive value results in an Object being created. That object (including its prototype chain) is then enumerated over.

Boolean.prototype.crap = "useless";
for(var prop in true) { alert(true[prop]); }
if(!delete Boolean.prototype.crap) 
    alert("uh oh :(");

Attempting to enumerate over a primitive will result in the evaluation of the primitive in creation of an object (§9.9), just as is with the property access operators, e.g. true[ prop ] or true.valueOf() === true or 1.1255.toPrecision(4);

The internal ToObject method, in the above case, will return a Boolean object holding the value true. The property named crap is resolved in Boolean.prototype, holding the string value "useless".

How About a String?

A string value might seem to be iterated over (with some practical usefulness). In that case, a String object would be created, holding the original string value; you wouldn't actually be iterating over a string value; it would just seem like it.

The difference between a String object and a string value, is explained in my entry How Property Access Works.

Practical Example of Iteration

Objects which support some form of sequential indexing of properties can be practically iterated over (§12.6.3).

JavaScript provides two types of built-in objects that fit that description.

String
Property values are characters. indexed by a numerical String starting at "0"
Array
Property values are anything. Indexed by a numerical String starting at "0"

Iterate over an Array

An Array is an object that is specialized by its length property, it's [[put]] method, and its literal initializer syntax. Array.prototype is sometimes modified to add features that are not supported in certain browsers (like Array Extras, supported in Webkit and Gecko, but not Opera 9.2 or IE 7).

Because Array.prototype is often modified, it is not safe to enumerate over an Array (unless you have a sparse array and know how hasOwnProperty works).

Non existent properties that need to be added by programmer-defined code do not exist, and therefore, cannot have the DontEnum attribute. For example, Array.prototype.some does not exist on Array.prototype in IE or Opera, but is non-enumerable, native code in other browsers.

To avoid enumerating over a property in the prototype chain, a cautious programmer will avoid using for in on an Array, and use hasOwnProperty when he does use for in.

Library authors might consider adding only the top-level Extras, e.g. Array.some to browsers that don't have Array.some, and leaving the prototype alone.

Iterating Over a String

When the string value is converted to a String Object, the characters in the value are accessible through the object.

String.prototype has a method to access character properties: charAt (§15.5.4.4).

Here's where it gets interesting.

Gecko and Webkit implement another way access to String index properties: The property access operators [ ]. This is not part of the official specification.

String's [[Put]] method §8.6.2.2 is specialized in Webkit. This seems to create some problems, as can be shown in an example.

Example Using [] on a String object

(function(){
    String.prototype['2'] = "prot";
    String.prototype['4'] = "prot4";
    a = new String("012");
    a[1] = "one";
    a[11] = "eleven"
    var result = [];
    for(var i = 0; i < a.length; i++)
        result.push(a.charAt(i) + ", a['"+i+"'] = "+ a[i]);

    result.push(a.charAt(4)  // Should be "".
        + " , a['"+ 4 +"'] = "+ a[4] );
    
    result.push(a.charAt(11)  // Should be "".
        + " , a['"+ 11 +"'] = "+ a[11] );

    return result.join(String.nl);
})();
Result Expected
0, a['0'] = 0 1, a['1'] = one 2, a['2'] = 2 , a['4'] = prot4 , a['11'] = eleven
Browser: Internet Explorer Mozilla Opera 9.2 Safari 3
Result: 0, a['0'] = undefined 1, a['1'] = one 2, a['2'] = prot , a['4'] = prot4 , a['11'] = eleven 0, a['0'] = 0 1, a['1'] = one 2, a['2'] = 2 , a['4'] = prot4 , a['11'] = eleven 0, a['0'] = 0 1, a['1'] = one 2, a['2'] = 2 , a['4'] = prot4 , a['11'] = eleven 0, a['0'] = 0 1, a['1'] = 1 2, a['2'] = 2 , a['4'] = prot4 , a['11'] = eleven

String.prototype.charAt works consistently in all browsers. The property access operator results are more interesting.

In Gecko and Opera, property access ([], §11.2.1) on a String checks the object for a property by the name given. If no property is found, if the string value of the property name is integral, charAt (or its equivalent) is called, passing the property, e.g aString[1.0] is evaluated as aString['1'] (§15.5.4.4).

Webkit has the opposite result. Webkit seems to have broken [[Put]] for String.

Observations on Property Access

Mozilla and Opera
  • a['0'] returns the character at index 0, "0". (goes through charAt)
  • a['1'] is resolved to a property, the value "one" is returned.
  • a['2'] does not find a property on the object itself and does not call [[Get]]. charAt is called.
  • a['11'] is resolved to a property, the value "one" is returned.

Gecko's [[Put]] results in a property being added to the object. The property will not be returned in charAt, but will be returned with [ ] (§11.2.1).

Webkit

Webkit seems to have a special algorithm for [[Put]] on String. A property P seems to be added to a String only when ToNumber(P) is greater than the String's length.

The approach Webkit takes is a bad one. I cannot see any reason why the Webkit team did this. A specialized [[Put]] might be the result of the design under the hood and was a preventative measure to keep the string from being modified (guess).

Unlike an Array, a String's length property is ReadOnly.

So we've covered enumerating over a primitive (useless), iterating over an String and an Array. What about iterating over an object?

Iterate over an Object

Its not usually practical, but it is possible

var nlist = {
  n : 1,
  nn: 2,
  nnnn: 3,
  nnnnnnnn: 3
};

if(! "console" in window) {
    document.title = "log: ";
    window.console = { log : function(s){ document.title += s; };
}
var result = "";
for( var p = "n"; nlist.hasOwnProperty(p); p+=p ) {
    console.log(nlist[p]);
}

Iterating over a BitSet as a String could be useful.

It would be possible (possibly even useful) to iterate over (or use Array or String extras with) a BitSet object. Fortunately, it's not that hard to write a BitSet class.

Enumeration is a Problem

Nearly every JavaScript library attempts to address the language limitations and browser bugs. Enumeration needlessly complicates Object. The libraries painfully show this to be true.

ECMAScript could instead provide Enum, SortedSet<T>, Map<T>, ListIterator<T>. These collections would provide safer, more powerful alternatives to the current problem.

I've provided more analysis on the problem of enumeration in my article, Enumeration and Object Oriented JavaScript

Posted by default at 6:12 PM in JavaScript

Type Checking in JavaScript Wednesday, 10 October 2007

So how do you tell if an object is a function?

What's wrong with typeof?

typeof o == "function";

Some browsers have unexpected behavior when using the typeof operator with Host objects.

For example, in Safari, typeof aNodeList == "function" bug 14547.

Mozilla returns "function" for typeof on RegExp objects (bug 14547), and object elements (bug 268945, bug 296858).

The instanceof Operator

The more flexible alternative to typeof is instanceof.

    return it instanceof Function;

Unfortunately, instanceof will not return the desired result across frames. Each window has a different global object, so Function in frame 1 is not Function in frame 2.

When working across frames, the constructor property and isPrototypeOf will have the same problems as instanceof.

For example, an iframe with a function f, and the following code in the parentWindow:

var i = document.getElementsByTagName("iframe")[0];
var iframeWindow = i.contentWindow;
iframeWindow.f instanceof Function; // false
typeof iframeWindow.f == "function"; // true;

It should be easy to know what an object's type is. Why does JavaScript make this so hard?

What Do the Libraries Do?

Dojo had the idea of depending on a combination of the userAgent string, the return value of the object's toString method, and implicit type conversion that happens with the Equals Operator §11.9.3.

if(dojo.isBrowser && dojo.isSafari){
 // only slow this down w/ gratuitious casting in Safari since it's what's b0rken
  dojo.isFunction = function(/*anything*/ it){
    if((typeof(it) == "function") 
      && (it == "[object NodeList]")){ return false; }
  return (typeof it == "function" || it instanceof Function); // Boolean
  }
}else{
  dojo.isFunction = function(/*anything*/ it){
    return (typeof it == "function" || it instanceof Function); // Boolean
  }
}

This is to address Safari's unreliable results when using the typeof operator on one particular host object.

The function will fail in Safari 3 when passed an HTMLCollection (try typeof document.links). It will fail for any other object that Safari thinks implements call.

The problem is not that dojo forgot to check for HTMLCollection. Web developers should not need to know such obscurities.

jQuery : isFunction

The jQuery code stumbles on this problem:

// This may seem like some crazy code,
// but trust me when I say that this
// is the only cross-browser way to do this. --John

  isFunction: function( fn ) {
    return !!fn && typeof fn != "string" && !fn.nodeName && 
      fn.constructor != Array && /function/i.test( fn + "" );
  },

Lets break down this crazy code to see what it's doing.

( !!fn ).......................convert to boolean value. null, et c are out
( typeof fn != "string" )......not a string value (String objects pass here) 
( !fn.nodeName )...............Not an object with a truthy nodeName property
( fn.constructor != Array )....Not an Array object constructed in this frame
( /function/i.test( fn + "" )..toString contains "function"

Does it work?

// Case 1: testObj is not a function. 
var testObj = {
    toString: function() {
        return "This object contains only one function."
    }
};

// Case 2: testString is not a function.
var testString = new String("hey! This is a String, not a function!");

// Case 3: WidgetFactory is a function.
function WidgetFactory( id ){ }
WidgetFactory.toString = function() { return "WidgetFactory"; };

// Case 4 : An Array in the frameWindow is not a function.
// var someArray = [ function(){}, "Simplify functional testing." ];

// Here's the test:
var frameWindow = document.getElementsByTagName("iframe")[0].contentWindow;
isFunction( testObj ); // true.
isFunction( testString ); // true.
isFunction( WidgetFactory ); // false.
isFunction( frameWindow.someArray ); // true, contains "function" (twice).

Doesn't work that well, does it?

Running code doesn't lie. All four tests failed.

The jQuery function will give the correct result sometimes, but not in any of the cases above. This is because jQuery is relying on the result from the function's toString.

These cases are quite obvious and likely cases where the above method will fail. Nothing tricky about it.

The value returned by toString should not generally be relied upon, and especially not with a Host object.

JavaScript forces developers to rely on toString with Date or Number objects. This is an API design of the language that goes against convention. Such design should not influence a JavaScript library author's design decisions.

The disadvantage of specifying the format of the toString return value is that once you've specified it, you're stuck with it for life.—Joshua Bloch, quote from Effective Java

Method toString should not, generally speaking, be relied upon. Instead, toString should be used for diagnostic messages.

String Objects are Not string Values

Web developers sometimes get confused with string values and String objects. My tutorial on how property access operators work, clearly explains this fundamental concept.

Here's another example from Dojo.

dojo.isAlien = function(/*anything*/ it){
  // summary: 
  // Returns true if it is a built-in function or some other kind of
  // oddball that *should* report as a function but doesn't
  if(!it){ return false; }
  return !dojo.isFunction(it) 
    && /\{\s*\[native code\]\s*\}/.test(String(it)); // Boolean
}

Besides conjuring up images of little green men, function isAlien returns true for anything that isFunction returned false for and also contains "[native code]" in the object's string value. The intent seems to be to check if a function might be a function even when typeof failed.

I have a very hard time believing that any unmodified built-in function does not return "function" for typeof. The comment needs more detail. It would be interesting to see the dojo comment proven true. At least they put an explanatory comment in.

The comment implies that a built-in function might not return "function" but does not provide a case where this is true. The code is based on that assumption and assumes that such an object's String value will contain "[native code]".

This approach suffers from the same problems that jQuery's isFunction suffered from. If any object's string value contains "[native code]", isAlien will return true. Granted, "[native code]" is less common than "function", but buying vowels isn't the answer.

Function isAlien provides a way for non-callable objects to slip through as functions. It's inclusive, like the jQuery function, and just as dangerous. It does this by relying on [native code] making deductions about the Object based on its toString's return value.

Return values from toString should not, in general, be relied on

If it walks like a duck and quacks like a duck, it must be a duck.

How to Determine if an Object is Really a Function

Functions are unique in a few ways. The return value for toString is not one of them.

It seems safer to introspect the object based on a unique characteristic.

Update - Oct 13

A solution is needed. Here is one more offering, introspecting the constructor property, accompanied with a testcase.

function isFunction(fn) {
  if(typeof fn != "function") return false;
// Now check the constructor property. 
  if(typeof fn.constructor != "function") return false;
// If constructor is Function, then constructor.prototype will have call.
  return fn.constructor.prototype.hasOwnProperty( "call" );
}

That's some ugly looking code, huh? Who has a better one?

Try to provide case where the testcase will fail.

We can make more functions for isArray, et c. What a pain in the ass. This should be easy. JavaScript makes it hard.

Type Checking - the Status Quo

JavaScript needs a way to perform equivalence check. This might be helpful for cross frame issues.

JavaScript provides instanceof and typeof, but neither are sufficient

instanceof fails across frames. It doesn't work with primitives, e.g. "foo" instanceof String is false.

The typeof operator has a limited number of return types. typeof doesn't support all of the built-ins properly (e.g. Array, Error, Date, and null are all "object") and doesn't support any user-defined constructors. Typeof is allowed to return an implementation-dependent value for host objects. Safari conforms, but in an annoying way that causes problems.

Type Result
Undefined "undefined"
Null "object"
Boolean "boolean"
Number "number"
String "string"
Object (native and doesn't implement [[Call]]) "object"
Object (native and implements [[Call]]) "function"
Object (host) Implementation-dependent

Internet explorer includes an additional "unknown".

Typechecking is an area that JavaScript, as a language, needs to improve.

Update...

Brendan just posted on the ES4 List:

The is operator tests universal or Platonic type, which involves shared, immutable type descriptors that do not vary across windows or frames. So

(it is Callable)

That looks very useful. Immutable objects are powerful building blocks for other object types. Looks like there might be the possibility of other callable types (or subclass of Function) in ES4.

Posted by default at 4:57 PM in JavaScript

How Property Access Works Friday, 5 October 2007

It's important for web developers to understand how the property access operators work. Here's a javascript basic lesson that every web developer should know:

Using an XML Reader?

The live JavaScript code won't run in your reader, but can be run on the site

(function(){
var a = "A String?";

a.isString = true;

return a.isString;
})();

What should be returned? Will it be the value true?

undefined

What happened to the isString property?

To answer that question, you need to know how the property access operator works.

The property access operator converts the value of its left hand operand (if it is a primitive) to an object and gets the property off that object.

Effectively, the above example is equivalent to:

    var a = "A String?";
    Object(a).isString = true;
    return Object(a).isString; // undefined

An object is created, assigned the property isString with the value true, and then becomes inaccessible by program code.

How about assigning properties to String objects?

(function(){
var a = "Axel";
a = new String(a);

a.foo = 123;

return a.foo;
})();
123

Variable a is now a String object. Objects can have properties.

The same conversion does not, unfortunately, take place with the instanceof operator.

(function(){
    var a = "Al Gore";
    return a instanceof String;
})();
false

It is still possible to check the constructor property of the object that is created by the property access operator.

(function(){
    var a = "avatar";
    return a.constructor === String;
})();
true

The typeof operator returns "string" for string values and "object" for String objects.

(function(){
    var a = "apple";
    var b = new String("bork");
    return typeof a + "; " + typeof b;
})();
string; object

Primitive values are not objects. Don't let the property access operators fool you.

Core JavaScript 1.5 Reference:Global Objects:String
Posted by default at 3:09 AM in JavaScript

Opera Clobbers Object.prototype with FunctionExpression? Wednesday, 3 October 2007

(function(){

propertyIsEnumerable = function(){ alert(123); };
( {} ).propertyIsEnumerable( "name" );
})();

In opera 9.2, both Mac and Windows, I get the alert.

This happens when adding a FunctionExpression property to the global object. The problem only happens when the property name corresponds to a native method.

Hopefully Futhark, Opera 9.5's new script engine that will replace linear_b, will address this problem.

Posted by default at 8:01 PM in JavaScript

How To Get the Largest and Smallest Valued items in an Array Thursday, 13 September 2007

John Resig had the idea of using Math.max to get the highest, valued item in an Array to get a Fast JavaScript max/min: Math.max.apply( Math, anArray );

John's Example:

Array.max = function( array ){
    return Math.max.apply( Math, array );
};
  
Array.min = function( array ){
     return Math.min.apply( Math, array );
};

Function.prototype.apply accepts a thisArg and an Array.

Math.max, and Math.min methods both accept a variable number of arguments (§15.8.2.11).

Let's try John's technique on objects.

var a = {valueOf:function(){return 10;},toString:function(){return "letter a";}};
var b = {valueOf:function(){return 3;},toString:function(){return "letter b";}};
var c = {valueOf:function(){return 17;},toString:function(){return "letter c";}};

var abc = [a,b,c];

// Pass null as the thisArg
alert( Math.max.apply( null, abc ) );

Result?

Whoops, what happened? We got a number, not a String.

This is because Math.max returns a number value, not an Object. (This should not be very surpising).

A Useful Technique?

Not really. If you have a value object that can be reconstructed by it's numeric value, it could work though. Let's try Date.

Last Date

var day = 1000 * 60 * 60 * 24;
var yesterday = new Date(new Date() -day );
var today = new Date();
var tomorrow = new Date(new Date() - (-day) );

var days = [yesterday, tomorrow, today];

// pass in null for thisArg. 
// Only a very poor implementation would choke on a null thisArg.
var latestDay = Math.max.apply(null, days);

alert("latestDay = " + new Date(latestDay));

So it works for Dates. It can be useful on some cases, but not for all arrays. Doesn't belong on Array.max.

String?

It won't work with String, either. Again, no surprise here.

var a = "apple";
var b = "banana";
var c = "cherry"

var abc = [b,c,a];

alert( Math.max.apply( null, abc ) );

NaN Again, no surprise here.

How to sort an Array

var a = "apple";
var b = "banana";
var c = "cherry"

var abc = [b,c,a];
var len = abc.length;
var sorted = abc.sort(); 
alert("max: " + sorted [ len-1 ] ); // last, sorted  item.

How ECMAScript processes Array.sort and Math.max

There's a lot of confusion about ECMAScript. This is due to many things. One reason is the official spec is in PDF. Had the spec been written in HTML, it would be easier for other developers to link to and discuss how confusing it is, in the spec's own terms, hopefully elucidating.

I'm here to talk about the spec and I'm linking Bob Clary's HTML Version of ECMA-262.

Math.max calls ToNumber which calls ToPrimitive on the object, which then calls DefaultValue with the hint Number.

Array.sort(comparefn) (§15.4.4.11) use the SortCompare algorithm. I've abbreviated this here, omitting the behavior that allows Array.sort to be generic (generics deserve another topic).

SortCompare

...
16. Call ToString(x).
17. Call ToString(y).
18. If Result(16) < Result(17), return -1.
19. If Result(16) > Result(17), return 1.
20. Return +0.
NOTE 1

Because non-existent property values always compare greater than undefined property values, and undefined always compares greater than any other value, undefined property values always sort to the end of the result, followed by non-existent property values.

NOTE 2

The sort function is intentionally generic; it does not require that its this value be an Array object. Therefore, it can be transferred to other kinds of objects for use as a method. Whether the sort function can be applied successfully to a host object is implementation-dependent.

Array Generics

Somewhat oustside the scope of this entry, but it's a useful technique, for example, to call Array.sort( compareFn ) on a NodeList. (this won't work in IE). There are many interesting aspects to Array generics.

ECMAScript ToString internal method

(not to be confused by Object.prototype.toString)

We can see that what happens, when the internal ToString is called, that calls ToPrimitive and ToPrimitive calls DefaultValue. This is the same behavior we saw with Math.max. In fact, anywhere you see ToString in the ECMAScript specification, you can expect the same set of steps, so it's important to understand this thoroughly.

How ECMAScript's DefaultValue Works

§8.6.2.6 [[DefaultValue]] (hint)
When the [[DefaultValue]] method of O is called with hint String, 
the following steps are taken:
1. Call the [[Get]] method of object O with argument "toString".
2. If Result(1) is not an object, go to step 5.
3. Call the [[Call]] method of Result(1), 
with O as the this value and an empty argument list.
4. If Result(3) is a primitive value, return Result(3).
5. Call the [[Get]] method of object O with argument "valueOf".
6. If Result(5) is not an object, go to step 9.
7. Call the [[Call]] method of Result(5), 
with O as the this value and an empty argument list.
8. If Result(7) is a primitive value, return Result(7).
9. Throw a TypeError exception.
When the [[DefaultValue]] method of O is called with hint Number, 
the following steps are taken:
1. Call the [[Get]] method of object O with argument "valueOf".
- 29 -
2. If Result(1) is not an object, go to step 5.
3. Call the [[Call]] method of Result(1), 
with O as the this value and an empty argument list.
4. If Result(3) is a primitive value, return Result(3).
5. Call the [[Get]] method of object O with argument "toString".
6. If Result(5) is not an object, go to step 9.
7. Call the [[Call]] method of Result(5), 
with O as the this value and an empty argument list.
8. If Result(7) is a primitive value, return Result(7).
9. Throw a TypeError exception.

When the [[DefaultValue]] method of O is called with no hint, then it behaves as if the hint were Number, unless O is a Date object (see 15.9), in which case it behaves as if the hint were String. The above specification of [[DefaultValue]] for native objects can return only primitive values. If a host object implements its own [[DefaultValue]] method, it must ensure that its [[DefaultValue]] method can return only primitive values.

Posted by default at 2:33 PM in JavaScript

Form Serialization, Round II Monday, 10 September 2007

Dion Almaer posted a question around 4AM this morning:

Will browsers ever implement a richer type="file"? Do we need to spec out a better way to do this?

Problem

No standard, reliable way to get FORM data to an Ajax request.

Current Usage

The need for Form Serialization has manifested itself strongly in all of today's popular Ajax libraries, however:

  1. nearly all of them get it wrong
  2. the code is necessarily large and complicated
  3. none of them serialize a file

The need for File serialization,is being standardized in the File and FileList specifications [File Upload], and is implemented in Mozilla Firefox [bug 371432]. File serialization now makes it possible to send a file using XMLHttpRequest, although the functionality has not gotten mainstream acceptance (too early).

Most JavaScript libraries will create and then target a hidden IFRAME when a FORM to be serialized for an Ajax request contains a file input. This has some drawbacks, as explained in the Form Serialization proposal.

Proposal

Add to HTMLFormElement interface, the following methods:

The HTMLFormElement interface

interface HTMLFormElement {
  string getDataSetString(); raises FileException
  string toJSONString(); raises FileException
};

Both methods will return a serialized representation of the FORM's successful controls.

Files will be serialized according to the [File Upload] specification. This is implemented in Mozilla Firefox [bug 371432].

toJSONString returns a JSON string with keys as names of successful controls and values as an array containing values of their associated key. For successful conrols that have the same name (such as OPTION or checkbox), the value is an array, so to keep the API consistent, we use an array for every value, even when the array contains only one item.

getDataSetString returns a URI string defined by HTML FORM specification.

Background:

Many Ajax apps use forms to provide a UI to model data. There are some benefits to this approach:

  • More explicit "stub" on the client. The URL and params are in the content layer (in the FORM).
  • can be used in a degradable approach for non-Ajax clients.
  • Entities can modeled on the client using standard UI controls

Current Usage

The need for Form Serialization has manifested itself strongly in all of today's popular Ajax libraries, however:

  1. nearly all of them get it wrong
  2. the code is necessarily large and complicated
  3. none of them serialize a file

W3C Proposal?

I have created an Form Serialization proposal page that documents the problem. Please read this; it will change more than this blog entry.

Contribute!

Web developers who are interested in Form Serialization, JavaScript, Ajax, and JSON will want to read the Form Serialization recommendation document. I have included the current technological solutions, how they manage the problem, and also considered other ideas that might solve the problem. I will consider other reasonable proposals and include them in the document.

Posted by default at 2:36 PM in Technical Reccommendation

CSSEditor - A StyleSheet Editor Wednesday, 8 August 2007

I have a CssEditor editor project that I started a few years ago but never finished. It needs to be finished and it needs a home.

What is the CSSEditor?

The CSSEditor provides a User Interface that allows the user to edit the actual rules in the styleSheet.

If you are not using Mozilla Firefox, you won't be able to try the StyleSheet Editor.

CSSEditor

The StyleSheet Editor is intended as a CMS bolt-on so that you can edit the style separately from the content.

How it Works

The CSSEditor has a styleSheet property that is an actual stylesheet, and uses the DOM Style Modules.

It uses a rule-mapping, performing a similar algorithm that the browser applies. The mapping matches an element to its most specific matching rule (as opposed to matching multiple RuleSets. (This is facilitated by Dean's CSSQuery)

This allows the user to select an element (by clicking).

When an element is selected, the most specific selector in the stylesheet is matched to that element and the CSSEditor's components are populated with the property: value pairs of the selector's corresponding RuleSet in the styleSheet.

I'll admit it may sound a bit complex. This really needs UML diagrams. It needs a lot of things, including being finished.

CSSEditor State Management

The CSSEditor has three states:

EditState
An element is being edited
SelectionState
User can select an element to edit
DisabledState
the CssEditor is disabled. No element can be selected. No element is being or can be edited.

State Change Management

State can change in the following conditions:

From SelectionState to EditState
By selecting an element (by clicking)
From DisabledState to SelectionState
By clicking on the panel's icon:  • 
From SelectionState or EditState
By clicking on the panel's icon:  • 

What it Needs

The CSSEditor needs a lot of work!

  • Server Integration
    • XMLHttpTransport
    • service ( Java, C#, or PHP )
  • Code Cleanup
  • Functional and User Testing
  • Background Image service (Flickr?)
  • Tooltips
  • QuickHelp
  • Developer Documentation and UML for the tricky parts
  • Bug Tracking
Posted by default at 1:04 PM in Widgets

Form Serialization Tuesday, 7 August 2007

ECMAScript 4 will provide two new methods for working with JSON data. This has also been previously proposed to Mozilla (bug 340987).

 Object.prototype.toJSONString
 String.prototype.parseJSON

My idea is to propose native form serialization for Mozilla and Webkit.

The major js libraries provide some sort of functionality for serializing forms. Why not let the browser do it?

 HTMLFormElement.prototype.toJSONString
 HTMLFormElement.prototype.getDataSetString

HTMLFormElement.prototype.toJSONString would return an object literal that contains the enabled (not readonly) form element names as keys and an array for each key's value.

aForm.getDataString() would return a serialized data set representation of the form's successful controls. It would look just like what you see in a GET query string or a POST body.

This is only a one-way trip. It wouldn't cover setting a form's values from a JSON object.

What do YOU Think?

Should the browser provide access to getting the serialized data that it sends?

Posted by default at 2:00 PM in Uncategorized

Poll Timer Friday, 3 August 2007

Nicholas Zakas found an unusual use of a poll-timer to work around Internet Explorer's lack of (or useless) resizeEnd event.

Internet Explorer actually does have a onresizeend event, but it is for contentEditable objects, so won't work as you might expect.

I created some activity diagrams to explain what a poll-timer is, Nicholas' variation, and a parameterized variation. Code will obviously be more detailed; diagrams are just for the idea and concept.

Poll Timer

A poll-timer continually polls to see if a condition exists, then fires a signal when it the condition is found.

Poll Timer

Resize kicks off the first timer and sets hasResize = true continually.

Once hasResize is found false, the timer won't reset. This type of timer can be implemented with setInterval quite nicely.

Poll-timers are basic and fundamental.

In the resize event, we are checking for absence of a condition: no resize fired within a duration.

The caveat: The final delay time will range from 251ms to as long as 500ms ((2 * DURATION) - 1ms).

Time Resetter (Nicholas' "Downshift" approach)

Nicholas probably saw this and decided to continually reset the timer instead. The trick is to set a timer for a duration after each resize event hits, but first clear the previous timer. Each resize will also clear any preexisting timers.

Time Resetter

This diagram calls fireEvent to produce an observable signal, instead of _process( ), as I feel that it is appropriate.

Parameterized Poll Timer

Back to the poll timer.

Since Date is very cheap, and threads are more CPU intensive, then it might be more efficient to set a timeStamp onresize. This would allow us to set a new thread after the old one expired, and only if necessary.

It's more along the lines of a "traditional" poll timer which appeared in the late 90s' DynLayer API, as diagrammed above.

Parameterized Poll Timer

Which one?

Testing is important, and this is merely conjecture. All three should work. The first two are bone simple. The first poll-timer will not fire it's event at a precise moment after the resize end. The parameterized poll-timer will, but is a little more complicated. The parameterized poll-timer should require fewer CPUs (Date is very cheap), but we won't know until it's tested.

I see Nicholas has also developed a testing framework for YUI, (though I have yet to investigate this). For testing user interaction, I feel that Selenium does a thorough job.

Posted by default at 8:12 PM in Uncategorized

Defensive Copying Thursday, 2 August 2007

Background

The following article on JavaScript is based on an concept that Josh Bloch introduced in Effective Java. If you're not a Java programmer, you can still benefit from reading this book.

What is Defensive Copying?

Defensive copying is about developing library code that is safe for clients to use and harder for them to accidentally break.

You must program defensively with the assumption that clients of your class will do their best to destroy its invariants. This may actually be true if someone tries to break the security of your system, but more likely your class will have to cope with unexpected behavior resulting from honest mistakes on the part of the programmer using your API. [Bloch]

Honest Mistakes

In JavaScript, such honest mistakes are probably more common than in a safe language like C# or Java. This happens for a couple of reasons.

One reason honest programming mistakes occur is that JavaScript is flexible and does not offer as many safety checks when a script is interpreted. Another reason could be that JavaScript is used by web developers who aren't necessarily programmers and programmers who have superficial JavaScript knowledge.

When working with mutable objects as a composite, if you provide a 'public' accessor method to the internal object, return a copy of that object.

JavaScript 1.6 doesn't have private modifiers, so we just use an underscore prefix.

function CookieManager() {
  // Private.
  this._cookies = [];
};

Unsafe Code

Consider the following prototype code, which might seem perfectly innocent.


// Broken, returns a 'private' member as mutable data.
CookieManager.prototype = {

    /** @member getCookies
     * @return {Cookie[]} Array of Cookie objects.
     */
    getCookies : function getCookies() {
        return this._cookies;
    }

If the client were to use this CookieManager class, s/he might cause a problem accidentally with the following code:

var cookies = CookieManager.getCookies();
cookies = cookies.reverse(); // Internal data has been changed!
var firstCookie = cookies.pop(); // Internal data has been changed again!

The simple solution is to return a copy of the array and note that in a comment, so it is clear to other programmers and to clients of the API.

Safer Code with Defensive Copying

// Fixed. Returns a defensive copy.
    /** @method getCookies
     * @return {Cookie[]} Array of Cookie objects.
     */
    getCookies = function() {
        return this._cookies.concat( ); // Return a defensive copy.
    };

This way, the client's modification to the array returned can't affect the behavior of your class.

The same is true for other objects, such as Dates:

getDateCreated : function getDateCreated() {
    return new Date( this._dateCreated ); // return a defensive copy.
}

It is equally important to make defensive copies for value objects that your client may provide to your API.

/** Sets the dateCreated to a new Date 
 * based on the value of aDate.
 * @param {Date} aDate
 */
setDateCreated : function setDateCreated( aDate ) {
    this._dateCreated = new Date( aDate ); 
}

It is not always appropriate to make a defensive copy of a mutable parameter before integrating it into an object. There are some methods and constructors whose invocation indicates an explicit documentation. [Bloch, Effective Java]

Not all objects need to be defensively copied. HTML Elements should not be copied. If it is clear that your class uses external objects as an aggregate, then the objects shouldn't be copied. If the objects are used as part of a composite or are value objects passed in, then copying them should be considered.

References

  1. Effective Java, Josh Bloch
Posted by default at 11:25 AM in JavaScript

Please Name Your Functions Thursday, 5 July 2007

I have updated this entry to address the JScript FunctionExpression Identifier bug (see below).

Giving a name to a function allows anyone to inspect the stack trace in any debugger and see the function's name, not "anonymous" or "no name".

A named function has an identifier following the function keyword.

The ECMAScript production for a FunctionExpression is as follows:

function Identifieropt ( FormalParameterListopt ){ FunctionBody }

Example

    var foo = function foo() {
    
    };

It is just as important to name constructor functions, too.

Don't do this:

/** 
 * @class Board. Used for creating board games (like checkers).
 * @constructor
 * @param x {Number} - number of horizontal squares
 * @param y {Number} - number of vertical squares
 */
com.example.game.Board = function ( x, y ) {

};

Do this instead:

/** @constructor
 * @param x {Number} - number of horizontal squares
 * @param y {Number} - number of vertical squares
 */
com.example.game.Board = function Board( x, y ) {

};

It is just as important to name methods, too.

Object Literals

com.example.game.Board.prototype = {
    /** 
     * @return squares {Array} a copy of the game's squares Array.
     * Modifying the returned array will have no effect on the game itself.
     */
	getSquares : function getSquares() {
		return this.squares.concat();
	}
	
	// DO NOT DO THIS. getScore should point to a named function.
	,getScore : function /*name goes here*/() {
	
	}
	
	,toString : function boardToString() {
		return this.name + ", " + this.getScore();
	}
};

Browser Compatibility

Safari <= 2 cannot handle named functions in object literals and will fail on interpreting the perfectly valid JavaScript file (bug 4698). The Safari bug has been fixed.

Internet Explorer has the unwanted side effect also interpreting a FunctionExpression as a FunctionDeclaration to the containing scope.

Microsoft is violating the ECMA-262 spec (pdf) (Citation of ECMA Spec: 1).

JScript FunctionExpression Identifier bug

The problem can be explained with an example:

    var meth = function ident() {
  // The identifier 'ident' may be legally referenced from within the function.
       alert( ident == meth ); // true, (actually false in IE);
    };
  // The variable 'ident' must not be affected by the function identifier.
    alert( ident ); // must be undefined, but not in IE.

The above examples illustrate that IE will leak a function declaration in the containing scope.

This seems to be due to faulty lexical parsing on the first pass. This is further evidenced by the following example that illustrates JScript overriding the first function declaration with the second.
if (true) {
  function f() { return true; }
} else {
  function f() { return false; }
}
// IE will choose "false" returning function.
alert('Conditionally Delcared Function: ' + f()); 

However, a FunctionExpression is not parsed on the first pass, and is correctly parsed in JScript (and JavaScript, of course).

var f;
if (true) {
  f = function () { return true; };
} else {
  f = function () { return false; };
}
// IE chooses the 'true' returning function this time.
msg('Conditional FunctionExpression: ' + f()); 

How to handle the JScript FunctionExpression Identifier bug?

To avoid unwanted side effects, either:

  1. Do not use an identifier, or
  2. wrap the code that uses identifiers in anonymous function

Example

(function(){ // Enclosing scope.

com.example.game.Board = function Board( x, y ) {
  ...
};
com.example.game.Board.prototype = {
        getSquares : function getSquares() {
            return this.squares.concat(); // defensive copy.
        }
};
})();

The above example will cause a scope to be created to "catch" a FunctionDeclaration that can be created in Microsoft JScript.

Consider Other Developers

You might be thinking: "It's not that big a deal, I can figure my code out." But will someone else be able to figure out a stack trace of "no name" functions in Firebug?

If You're Writing Library Code

You should name your functions.

It can be very difficult to pinpoint the actual function in the source code from a stack trace that contains anonymous functions referring to external library code that you did not write. When the code uses subclassing, it is even harder to find the actual method that caused the problem.

I sometimes have to resort to calling toString() on the function object in the debugger, copying that text, then searching for that text in my IDE. It works, but nobody should have to resort to those types of tactics.

Wouldn't it make more sense to just name the function in the first place? Something about an ounce of prevention...

If You're Writing Implementation Code

You should name your functions.

Most errors will start out in implementation code. If the first error in the stack trace is anonymous, it will be harder to debug and take more time.

Summary

Naming your functions is an easy way to make your code more explicit and easier to debug. Due to problems in Microsoft's JScript engine, FunctionDeclarations and FunctionExpressions with an Identifier must be used with care.

References

  1. From the EMCA-262 specification.

    NOTE

    The Identifier in a FunctionExpression can be referenced from inside the FunctionExpression's FunctionBody to allow the function to call itself recursively. However, unlike in a FunctionDeclaration, the Identifier in a FunctionExpression cannot be referenced from and does not affect the scope enclosing the FunctionExpression.

  2. ECMAScript's FunctionDeclaration versus FunctionExpression, by Hallvord R. M. Steen.
  3. Core JavaScript 1.5 Reference:Functions
Posted by default at 2:29 PM in Debugging

Lazy Load Singleton Friday, 22 June 2007

Overview

The lazy load Singleton is useful when your program must have at most one instance of an object.

UML
Singleton
INSTANCE : Singleton
getInstance() : Singleton

Forces

  • Your program needs to conditionally create an object in response to one or more events or a state changes.
  • More than one instance of that object would cause a problem.

The getInstance method should be invoked in a method call, often in an event handler. The event can also be an AJAX response event, such as FAILURE or COMPLETE. (Few AJAX Libraries use call subscription but instead introduce a dependency injection.)

If the instance is needed in an event handler, then the instance creation occurs if and when the event occurs. No instance will be created if the event handler is not called.

If the instance requires some initialization routine, lazy loading, there is an extra benefit of deferred instantiation. Otherwise, page rendering could be unnecessarily delayed by eager loading.

Typical Scenario / Motivation

Use cases for Singleton can be page or application objects.

Things that are unique to the page are likely to be widgets, such as Tooltip or Editor. Any object which may be only instantiated once and no more can benefit from using this pattern.

Things that are unique to the application are usually more related to the framework, such as an Event Broadcaster, or UserAgent checker, or AJAXConnector. In some cases, it won't be a problem to have two instances of an object, but would be undesirable. In such cases, it might be okay to just create one.

However, if it might cause a problem to have more than one instance of an object, then it is important to protect against creating more than one.

Example

This example uses a Tooltip. Having more than one tooltip at a time would be an error. We also might not need any tooltips. Using lazy initialization can work for this example. However, a class which has a lot of initialization might realize a benefit of lazy loading more.

/**
 * @class Tooltip shows a tooltip based on the element's title attribute.
 * captures the mouseover event on the document and checks the event target
 * to conditionally show a tooltip.
 * @private 
 * @final
 * @constructor 
 * @see Tooltip.getInstance();
 */
Tooltip = function Tooltip() {
    // This constructor can only be called by Tooltip.getInstance().
    Function.entryCheck( arguments.callee.caller, Tooltip.getInstance );
};

Tooltip.getInstance = function getInstance() {
    return this.INSTANCE || ( this.INSTANCE = new this() );
};

Tooltip.purgeInstance = function purgeInstance() {
    // Clean up all listeners.
};

/** Tooltip instance methods and properties.
 */
Tooltip.prototype = {
    ACTUATOR_CLASSNAME : "tooltip"
    ,show : function show() {}
};

What is purgeInstance()?

Defining purge instance is useful so that when the Singleton is unloaded, the listeners can be removed. For example, in a tab pane, you would have only one active pane at a time. If the pane were loaded with XHR, then it would be possible to clean up the page to avoid memory leaks (1), (2).

Benefits

The implementing class is clearly written and easy to read. It makes use of a named constructor and typed instance, whose constructor can be introspected via the constructor property.

First we see the constructor and at the very top of the constructor, we see the entryCheck telling us that the caller can only be Tooltip.getInstance. Next we see the getInstance method, then the prototype methods and properties which act on the instance.

Drawbacks

Requires extra function entry check.

Function entryCheck will not work in browsers that do not support Function.prototype.caller. The entryCheck function will not work in Safari <= 2 and Opera <= 9.

Implementation

To make Tooltip private, I've thrown in the call to Function.entryCheck and passed Tooltip.getInstance as the allowed entryPoint. That means that only Tooltip.getInstance can call the constructor without generating an error.

Now I'm going to explain how Function.entryCheck works.

Function entryCheck has been added onto the Function object as a method that is used by functions. This method could be just as easily added to its own package, if desired.

 /**
  * Function.entryCheck,
  * If the callee is not an allowed entryPoint, throw an error.
  * @param {Function} caller, the method's actual caller (we must check 
  * to see if it is allowed). 
  * @param {Array, Function} entryPoints, Array of Function objects or a single function
  * @param {String} errorMessage, if absent, a default error is generated.
  * @author gsmith
  */
Function.entryCheck = function entryCheck(caller, entryPoints, errorMessage) {
	var entryPointArray = [];
	if(entryPoints.constructor == Function)
		entryPointArray = [entryPoints];
	if( entryPointArray.indexOf( caller ) == -1 ) {
		if(!errorMessage) {
			var entryPointNames = Function.entryCheck.getNames( entryPointArray );
			  
			errorMessage = (arguments.callee.caller.getName()
			  + " constructor: entryCheck failed.\n "
			  + "Try: "
			  + entryPointNames
			  + " instead.");
		}
		throw new Error(errorMessage);
	}
};

Function.entryCheck.getNames = function getNames( functionArray ) {
	var names = [];
	functionArray.forEach(
		function(e) {
			names.push( e.getName() );
		}
	);
	return names.join(", ");
};

 /**
  * Function.prototype.getName,
  * Plucks the name off of a function. Avoid using anonymous functions.
  * @author gsmith
  */
Function.prototype.getName = function getName() {
	var s = this.toString();
	return s.substring( 0, s.indexOf("{") );
};

Misconceptions

There is a common misconception that using a Singleton will keep the global namespace clean. In fact, a singleton, by definition, is a globally accessible object. Using a Singleton won't keep the global namespace any cleaner than using a just create one approach.

Encapsulating methods and properties in an object will keep the global namespace cleaner than using a lot of top level functions and variables.

Moving on

Another approach would be to encapsulate this Singleton aspect into a class. This class would make a constructor into a singleton by wrapping the call and using before advice.

Writing advise in JavaScript is not hard, but debugging advised methods can be, so I try and avoid it. It is important to name your functions, and this is especially true for AOP.

References

  1. JScript Memory Leaks, by Douglas Crockford
  2. The Internet Explorer Memory Leak Problem, on Jibbering.
Posted by default at 1:09 PM in JavaScript

Using Closures to Keep the Global Namespace Clean Saturday, 17 December 2005

I've recently adopted technique to keep the global namepsace clean. The technique is to use an Anonymous Function for a closure.

Problem: A set of globally accessible functions or objects share a common need for a helper function or variable that is not needed anywhere else.

Solution: Create an anonymous function to encapsulate the global and functions or objects and limit the scope of the helper functions and variables by declaring them locally (by using the var or function in the start of the declaration.

Example:

(function() {
 // global methods.
    hasToken = function(s, token) {
        return getTokenizedExp(token,"").test(s);
    };
    removeClass = function(el, klass) {
        el.className = el.className.replace(getTokenizedExp(klass, "g")," ").normalize();
    };
    addClassConditionally = function(el, klass) {
        if(!getTokenizedExp(klass).test(el.className)) el.className += " " + klass;
    };

 // local variables and functions.
    var TokenizedExps = { };
    function getTokenizedExp(token, flag){
        return (TokenizedExps[token] ||
          (TokenizedExps[token] = new RegExp("(^|\\s)"+token+"($|\\s)", flag)));
    }
})(); // anonymous function immediately invoked with ().

Advantages: Easy to implement. Little change required. No performance hit.

Variables can be hidden in scope.

Consequences:

The local variables will not be accessible outside of the anonymous function. The long-term effect this can have is that if the need for those variables arises outside of the context of the anonymous function, then either 1) the those local variables will have to be made public, or 2) the code that needs those variables will need to be placed into the enclosing anonymous function.

Use this technique to clean up the global namespace.

By creating and invoking an anonymous function, we can define public objects or methods that use the enclosing function's local variables and functions.

Posted by default at 2:17 PM in JavaScript

String Manipulation: toCamelCase and toHyphenated Thursday, 24 November 2005

I just figured out a way to make toCamelCase and toHyphenated run a little bit faster. It also reduced my code by a few lines.

Performance tuning, for me includes refactoring (usually to patterns), and replacing algorithms with faster ones*. Since I'm doing a lot of development onmy project now, I'm also refactoring. I'm posting this thing up here because it's the fastest, most efficient way I could find. Good, free code Take it. Use it for whatever.

toHyphenated

function toHyphenated(s) {
	var exp = toHyphenated.exp;
	if(!exp.test(s)) return s;
 	return s.replace(exp, "-" +RegExp.$1.toLowerCase() );
}
toHyphenated.exp = /([A-Z])/g;

toCamelCase


function toCamelCase(s) {
	for(var exp = toCamelCase.exp; 
		exp.test(s); s = s.replace(exp, RegExp.$1.toUpperCase()) );
	return s;
}
toCamelCase.exp = /-([a-z])/;

RegExp allows you to reference matched parenthetical groups in RegExp.$1 (up through $9). You can also grab some properties such as RegExp.lastMatch, RegExp.lastParen (last parenthetical match, which can be the same as $1 or $2, depending on the number of parens and the input matched by the expression), RegExp.rightContext (string data following RegExp.lastMatch), and RegExp.leftContext (string data preceding RegExp.lastMatch).

The benefits of using RegExps:

  • Faster (except for in Opera) (test)
  • more concise

The drawbacks to RegExps is that they're sometimes difficult to understand or debug. Opera doesn't perform them faster and doesn't fully support RegExp.

Compatibility

Opera 8 doesn't execute RegExps very fast. It also doesn't support the lastMatch or lastParen properties.

*I'm kind of a revisionist, always changing things to try to improve them. Hey, it worked for Beethoven!

Posted by default at 10:27 AM in JavaScript

 

*AnimTree
*Tabs
*GlideMenus
*DragLib