Real World JavaScript Part 4

Specifying a function as string replacement

In EmberJS camelize method is used for converting a string to its loweCamelCase form.


  var STRING_CAMELIZE_REGEXP = /(\-|_|\.|\s)+(.)?/g;
  camelize: function(str) {
      return str.replace(STRING_CAMELIZE_REGEXP, function(match, separator, chr) {
        return chr ? chr.toUpperCase() : '';
      }).replace(/^([A-Z])/, function(match, separator, chr) {
        return match.toLowerCase();
      });
   }

Much like using double quotes to define a string literal forward slashes / are used for defining a regular expression literal. Inside the regular expression (\-|_|\.|\s) is representative of (x) pattern and it means match x and then remember it.

Hyphen in regular expression is a special character and it is used to represent a range. If we place a backslah before hyphen, it will make the hyphen to be treated as a simple literal character instead. x|y pattern matches either x or y.

Dot . is also a special character and a backslash makes it an ordinary one. s is not a special character and a backslash before it makes it a special one. \s matches a single white space character, including space, tab, form feed, line feed. So (\-||\.|\s) is matching and remembering -,,., or a space character.

Next is a + and it means match the preceding character one or more times.

Next is (.)?. ., a special character means any character and the parentheses makes it to be remembered for later use. ? mark means match the preceding character zero or one time.


  indiana_jones --> Matches _j
  indiana-jones --> Matches -j
  indiana.jones --> Matches .j
  indiana jones --> Matches  j
  indiana______jones --> Matches ______j

The first parameter passed to the replacement function is the matched substring. Then the remembered groups are passed as parameters. Finally, offset and the entire string will be provided as the parameters. Behind the scenes, the function will be invoked and retrun value will be used as the replacement. This could be chained to avoid the usage of complicated regular expressions and to ease the function readiness.

published on: 12 Jul 2013
A backslash (\) makes a special regex character a normal one
A backslash (\) also transforms normal character to a special one

Array Equality

Checking two arrays for equality using the == and === operators will fail as array is considered as an object in javascript and two instances of objects will not be equal.


  typeof([1,2,3]) // outputs "object"
  [1,2,3] == [1,2,3] // false
  [1,2,3] === [1,2,3] //false

One quick solution is to convert the arrays to string and then compare them. But the drawback of this method will be obvious after a couple of tries.


JSON.stringify([1,2,3]) === JSON.stringify([1,2,3]) // true
JSON.stringify(["1",2,3]) === JSON.stringify([1,2,3]) // false
JSON.stringify([,]) === JSON.stringify([null]) // true
JSON.stringify([undefined]) === JSON.stringify([null]) // true

But the caveat is JSON.stringify need not be capable of dealing with the circular references as demonstrated in the following.


  var a = [1];
  a.push(a);
  JSON.stringify(a) // Error: TypeError: Converting circular structure to JSON

The real solution is to compare the elements one by one and then decide whther the given arrays are equal or not. _.isEqual method from the lodash or UnderscoreJS library makes this task trivial.


  var a = [1];
  a.push(a);
  var b = [1];
  b.push(b);

  _.isEqual(a, b); // outputs true 

JSON.stringify accepts a replacer as second parameter and it could be used to overcome these type of circular reference errors.

Dynamic Function Creation

Functional Javascript library from Oliver Steele pushes the JavaScript capabilities to advanced functional programming level. One mind blowing feature of this library is the ability to transform string expressions to functions. This method is called “lambda”.


  "*".lambda()(2, 3); // returns 6 and uses implicit parameters
  "x * y".lambda()(2, 3); // returns 6 and uses explicit parameters
  "_ * 3".lambda()(2); // returns 6 and uses parameter placeholder

This feature set would not have been possible without the dynamic function creation facility in JavaScript. The essence of lambda implementation in functional javascript could be expressed as follows.


  String.prototype.lambda = function() {
      
      // parameters extraction code omitted 
      // expression parsing and creation code omitted
      return new Function(params, 'return (' + expr + ')');
  }

The function constructor accepts an optional list of parameters and the body of the new function (as string) as arguments. This function implementation will be slower than declaring and invoking a function but gives much control in pushing JavaScript capabilities.

Using Object.prototype.toString for confirming Array

ZeptoJS uses the instanceof utility to confirm the given object is an array as in the following code.


  function isArray(value) { 
    return value instanceof Array ;
  }

instanceof checking for the array will fail if the passed in array is created through another frame.

JQuery has a different approach.


// code modified to make it stand alone
var class2type = {};
function type ( obj ) {
if ( obj == null ) {
return String( obj );
}
return typeof obj = "object" || typeof obj = “function” ?
class2type[ {}.toString.call(obj) ] || “object” :
typeof obj;
}
jQuery.each(“Boolean Number String Function Array Date RegExp Object Error”.split(" "), function(i, name) {
class2type[ "[object " + name + “]” ] = name.toLowerCase();
});
JQuery uses toString approach to determine the type of Boolean, Number, String, Function, Array, Date, RegExp, Object, and Error.

UnderscoreJS too uses the toString approach for checking array.


  _.isArray = nativeIsArray || function(obj) {
    return toString.call(obj) == '[object Array]'; // where toString = Object.prototype.toString
 };

ZeptoJS gives high performance at the cost of accuracy, the proper way is much slower, and JQuery opts for the middle ground.

Knowing when to break the rules

Array literals are preferred over the array constructor in general and UnderscoreJS goes against this rule in its _.range implementation. This gives a performance advantage over the ususally advocated array literal syntax and in the case of range this deviation from the rule could be justified.


  _.range = function(start, stop, step) {
    if (arguments.length <= 1) {
      stop = start || 0;
      start = 0;
    }
    step = arguments[2] || 1;

    var len = Math.max(Math.ceil((stop - start) / step), 0);
    var idx = 0;
    var range = new Array(len);

    while(idx < len) {
      range[idx++] = start;
      start += step;
    }

    return range;
  };
This is part of a series of posts explaining JavaScript concepts in real world context. Previous parts of this series are – Part 1, Part 2, and Part 3 - Suhair
comments powered by Disqus