Comparison against null and undefined in Javascript

August 5th, 2008     ,     No Comments

Values in Javascript can come from

  • Objects - object reference
  • Strings - immutable, 16-bit USC-2
  • Booleans - true, false
  • Numbers - only 64-bit floating point, IEEE 754 (double)
  • null - empty object reference
  • undefined - unassigned variables and function arguments, missing object properties

Boolean evaluation of values using == and != operators

Values that are interpreted as false:

  • false
  • null
  • undefined
  • "" - the empty string
  • 0 - the number 0
  • NaN - not a number, result of undefined math operation

All other values and objects are interpreted as true.

Working with comparison operators

The == and != operators will do implicit type conversion of it's arguments to match their type. Since the null and undefined values are both equal with these operators, errors can occur when a missing object property is used in a null-comparison.

By using the === and !== operators, no implicit type conversion will be done. The values null and undefined are not equal using these operators. These operators can be used to check against missing properties and unassigned arguments among other.

Notice: Be careful if updating your old javascript code with === and !== for null-comparison tests. It might be that an undefined-comparison should have been used instead (or both). Failure to recognise these cases can introduce hard-to-catch errors.

References

Deep Pocket Layout - a tree data structure for UI layout

February 22nd, 2008     ,     No Comments

Introduction

Deep Pocket Layout uses a specialized tree data structure for layout of user interface elements (widgets). This data structure can be used for partitioning the screen-space, similar to the quadtree and kd-tree data structures, and uses the naming convention of border layout. In usage, it functions as a tree of either vertically or horizontally linked partitions.

In the following example, Deep Pocket Layout will be explained as a container widget, as part of an inheritance hierarchy of other widgets.

Description

The figure above shows the named "pockets" of the data structure. The outmost pockets are used for linking the Pocket Layout Widget (PLW) together with other PLWs. The center pocket can contain a PLW (thus forming a tree) or a different kind of Widget (leaf node). The center pocket specifies the screen-space, while the border pockets acts as internal links to other PLWs. When the left or right pockets are linked, they become part of a horizontal layout (top and bottom pockets must always be empty). Conversely, when the top or bottom pockets are linked, they become part of a vertical layout (left and right pockets must always be empty).

The first example below shows how a widget (of any kind) can be inserted by using the center pocket of the PLW.

PLWs can be arranged into either vertical and horizontal layouts. The figure below shows how a horizontal layout is started. Since P1 it the root of the tree, a new PLW P3 is created, replacing the center pocket content of P1. Then P2 is linked with P3, starting a horizontal layout.

The figure below shows how a vertical layout is started. This example is identical to the previous one, but starting a vertical layout instead. (The root constraint for P1 still applies.)

The final example shows how to split a PLW in a horizontal layout into a vertical. Here P3 is already part of a horizontal layout. A new PLW is created and replaced with P3s center pocket. The vertical layout is then started by linking P6 with P7.

The final deep pocket layout of this example is visualized in the following figure.

Summary

Some features:

  • Enables combination of tree-based vertical/horizontal layouts.
  • Allows the user to create/move any PLWs in the hierarchy, thus direct control over the complete layout with the use of drag-and-drop or resizing.
  • Control over screen space that is delegated within the tree hierarchy.
  • PLW linked in either a horizontal or vertical layout can adjust neighboring PLWs.

Implementation extensions:

  • The ratio of the screen space taken by linked PLWs can be adjusted. For this example it was "share equally".
  • PLW can visualize drag-and-drop targets for their pockets when dragging is in progress.

References

Code documentation for Javascript and Actionscript 3

January 1st, 2008     , ,     No Comments

Prerequisites for Actionscript documentation

Prerequisites for Javascript documentation

Actionscript 3 Code Documentation

Available commands:

/**
* Class description
*/
class-declaration.
/**
* Method description
*
* @see methodName
* @param paramName paramDescription
* @return returnDescription
*/
method-declaration.
/**
* Attribute description
*/
attribute-declaration.

Extracting code documentation:

asdoc.exe -source-path ../pathToSourceFiles/
-doc-sources ../pathToSourceFiles/
-output ../pathToDocumentationFiles/
-window-title "Title API Documentation"

Javascript Code Documentation

/**
 * The class description.
 * @author authorDescription
 * @extends className
 * @see className
 * @version versionDescription
 * @constructor
 */
class-constructor-declaration.
/**
 * The method description.
 * @see className#methodName
 * @param {paramDataType} paramName paramDescription
 * @return returnsDescription
 * @type returnDataType
 */
method-declaration.

Extracting code documentation:

perl jsdoc.pl ../javascriptSourceDirectory/

References for this post

Multiple Inheritance in Javascript

December 26th, 2007     , ,     No Comments

Notice

Javascript does not support multiple inheritance, so the given method will break the instanceof operator! Javascript checks inheritance by traversing the linked list prototype.__proto__ for occurences of the requested prototype. This means that one prototype can only contain one reference to another prototype and in effect only inherit from one prototype. By discarding support for the instanceof operator, multiple inheritance can be simulated.

A support function is available that provide similar functionality as the instanceof operator.

Description and implementation details

The function responsible for applying inheritance between classes works by copying all missing attributes/methods from parent class(es) to the inheritance class. In addition, metadata in the class constructors allows for isInstanceOf() comparisons to to made on objects.

/**
 * Check if object is instance of given class.
 *
 * @param {Object} object Object to check inheritance of.
 * @param {Function} classConstructor Constructor function to check inheritance against.
 * @return Boolean indicating success of comparison.
 * @type {Boolean}
 */
function isInstanceOf(object, classConstructor) {
   // Check for class metadata.
   if (object.constructor.inheritance === undefined || classConstructor.inheritance === undefined) {
      return object instanceof classConstructor; // Use standard inheritance test.
   }
 
   // Use inheritance metadata to perform instanceof comparison.
   return object.constructor.inheritance.fromClasses[classConstructor.inheritance.index] !== undefined;
}
 
/**
 * Applies inheritance to a class (first argument) from specified parent
 * classes (second, third, and so on, arguments).
 *
 * Notes:
 *  - This WILL BREAK the instanceof operator! Use the isInstanceOf() function instead.
 *  - Parent classes must be fully declared before calling this function.
 *  - Multiple classes will be copied in sequence.
 *  - Properties that already exists in class will not be copied.
 */
function applyInheritance() {
   // Validate arguments.
   if (arguments.length < 2) {
      throw new Error("Error while calling the function applyInheritance(). Not enough arguments given.")
   }
 
   // Check if classList has been created.
   if (applyInheritance.classList === undefined) {
      applyInheritance.classList = [];
   }
 
   // Create inheritance metadata for inheritence class.
   if (arguments[0].inheritance === undefined) {
      arguments[0].inheritance = {};
      arguments[0].inheritance.index = applyInheritance.classList.length;
      arguments[0].inheritance.fromClasses = [];
      arguments[0].inheritance.fromClasses[arguments[0].inheritance.index] = true; // class links to itself.
      arguments[0].inheritance.toClasses = [];
      applyInheritance.classList.push(arguments[0]);
   }
 
   // Iterate through all parent classes.
   for (var i = 1; i < arguments.length; i++) {
      // Create inheritance metadata for parent class.
      if (arguments[i].inheritance === undefined) {
         arguments[i].inheritance = {};
         arguments[i].inheritance.index = applyInheritance.classList.length;
         arguments[i].inheritance.fromClasses = [];
         arguments[i].inheritance.fromClasses[arguments[i].inheritance.index] = true; // class links to itself.
         arguments[i].inheritance.toClasses = [];
         applyInheritance.classList.push(arguments[i]);
      }
 
      // Update inheritance metadata.
      arguments[0].inheritance.fromClasses[arguments[i].inheritance.index] = true;
      arguments[i].inheritance.toClasses[arguments[0].inheritance.index] = true;
 
      // Iterate through all properties in prototype of parent class.
      for (var property in arguments[i].prototype) {
         if (arguments[0].prototype.hasOwnProperty(property) === false) {
            // Copy missing property from the parent class to the inheritance class.
            arguments[0].prototype[property] = arguments[i].prototype[property];
         }
      }
   }
}

Multiple inheritance example:

function Animal(name) {
   // Attributes
   this.name = name;
}
 
Animal.prototype.makeSound = function (soundOutput) {
   soundOutput.playSound("Unknown");
};
 
Animal.prototype.getHierarchy = function () {
   return "Animal";
};
 
function Petable(personality) {
   // Attributes
   this.personality = personality;
}
 
Petable.prototype.isCompatible = function (personality) {
   return this.personality == personality;
};
 
function Dog(name, personality, breed) {
   // Call parent class constructors.
   Animal.call(this, name);
   Petable.call(this, personality);
 
   // Attributes
   this.breed = breed;
}
applyInheritance(Dog, Animal, Petable); // Applied inheritance to Dog from Animal and Petable.

An example of how to override a method of a parent class:

Dog.prototype.makeSound = function (soundOutput) {
   soundOutput.playSound("Bark");
};

An example of how an overidden method can delegate to it's parent method.

Dog.prototype.getHierarchy = function () {
   return Animal.prototype.getHierarchy.apply(this, arguments) + ".Dog"; // Will return 'Animal.Dog'
};

An example of how to call a parent method.

Dog.prototype.isOwner = function (name, personality) {
   if (this.name != name) {
      return false;
   }
 
   return Petable.prototype.isCompatible.call(this, personality);
};

About calling function objects:
Two options are available when calling a javascript function object.

  1. Using the call() method which accepts as arguments a this object followed by one or more arguments to pass on (depending on the number of arguments in the called function).
  2. Using the apply() method which accepts as arguments a this object followed by an function arguments object.

References

Associative Arrays in Javascript

December 26th, 2007     ,     No Comments

Introduction

The associative array (also known as hash) is a variant of the ordinary array. Instead of being indexed by integer-based keys, the associative array uses string-based keys. The associative array is also missing the bookkeeping abilities of the ordinary array like sort(), splice() etc. Also note, in Javascript every object has the ability to act as an associative array.

Creating an associative array and filling it with some data

var container = {};
container["someKey"] = 3;
container["someOtherKey"] = someObject;
container["anotherKey"] = "Some text";

Accessing elements in the associative array

var element = container["someKey"];
container["aKey"] = someData;

Iterating through all the elements in the associative array

for (var key in container)
{
   if (container.hasOwnProperty(key))
   {
      var element = container[key];
   }
}

Here the hasOwnProperty() method helps in avoiding errors such as accessing an inherited property in the container object.

Deleting a key in the associative array

delete container[key];

Notice: Do not assign a null to the deleted key, as this will re-create the key and assign it the null value!

Checking if a key exists in the associative array

if (container["someKey"] === undefined)
{
   // Key does not exist.
}

References

Hello world!

December 25th, 2007         No Comments

This is a blog by Trond Olsen. It's mainly just a personal diary about programming stuff I've encountered. The content of the posts are not static and may change in the future.