Notes from JavaScript: The Good Parts

Essence of JavaScript: The Good Parts

Grammar

  • Use // instead of /* */ for comments.
  • JS number takes 64bits, string takes 16bits.
  • typeof produces one of the following: (typeof null will produce object)
    • number
    • string
    • boolean
    • undefined
    • function
    • object
  • JS falsy values:
    • false
    • null
    • undefined
    • The Empty string ''
    • The Number 0
    • The number NaN
  • The simple types of JavaScript are numbers, strings, booleans (true and false), null, and undefined. All other values are objects. Numbers, strings, and booleans are object-like in that they have methods, but they are ''immutable''. Objects in JavaScript are mutable keyed collections. In JavaScript, arrays are objects, functions are objects, regular expressions are objects, and, of course, objects are objects.
  • Use || operator the set default values. Use && operator to guard against undefined exception from throwing.
    var middle = stooge["middle-name"] || "(none)";
    var tstatus = flight.status || "unknown";
    if(data.more && data.more.inf) {...}
    
  • Do not put { at the beginning of the line,
    (function(){
      foo=function(){
        return
        {
          status:'on'
        };
      };
      return foo();
    })();
    // foo will return undefined instead of an object with a property named status.
    
  • Misc pitfalls
    typeof null // 'object'
    typeof undefined // 'undefined'
    alert(typeof NaN); // 'number'
    parseInt('16 age 320') // 16
    parseInt('08',10) // always pass radix parameter
    bject.prototype.toString.apply(ma) === '[object Array]' // test if ma is an array
    

Prototype and Reflection

  • Every JS object is linked to a prototype (Object.prototype) which it can inherit properties.
    if (typeof Object.create !== 'function') {
         Object.create = function (o) {
             var F = function () {};
             F.prototype = o;
             return new F();
         };
    }
    var another_stooge = Object.create(stooge);
    
  • Reflection
    // a property retrieved from prototype chain
    typeof flight.toString // returns 'function',
    // To check if an object has an property of its own
    flight.hasOwnProperty('number')
    
  • Enumeration
    // Instead of for ..in , use
    var i;
    var properties = ['firstname', 'surname', 'username'];
    for (i=0;i<properties.length; i++){
      var value = obj[properties[i]];
      // Get the results in the right orders ...
    }
    
  • Delete
    another_stooge.nickname    // 'Moe'
    // Removing a property from an object may allow a property from the prototype linkage to shine through:
    delete another_stooge.nickname;
    another_stooge.nickname    // 'Curly'
    document.writeln('log?');
    

Functions

  • Invocation of functions
    • The Method invocation pattern
      var myObject = {
          value: 0,
          increment: function (inc) {
              this.value += typeof inc === 'number' ? inc : 1;
          }
      };
      
      myObject.increment(  );
      document.writeln(myObject.value);    // 1
      
      myObject.increment(2);
      document.writeln(myObject.value);    // 3
      
    • The Function Invocation Pattern
      // Augment myObject with a double method.
      
      myObject.double = function (  ) {
          var that = this;    // Workaround.
      
          var helper = function (  ) {
              that.value = add(that.value, that.value);
          };
      
          helper(  );    // Invoke helper as a function.
      };
      
      // Invoke double as a method.
      
      myObject.double(  );
      document.writeln(myObject.value);
      
    • The Apply Invocation Pattern
      var status = Quo.prototype.get_status.apply(statusObject);
          // status is 'A-OK'
      
  • Variable number of arguments
    var sum = function (  ) {
        var i, sum = 0;
        for (i = 0; i < arguments.length; i += 1) {
            sum += arguments[i];
        }
        return sum;
    };
    
    document.writeln(sum(4, 8, 15, 16, 23, 42)); // 108
    

Exceptions

  • Exceptions
    // Exceptions are objects which at list have the name and message properties
    var add = function (a, b) {
        if (typeof a !== 'number' || typeof b !== 'number') {
            throw {
                name: 'TypeError',
                message: 'add needs numbers'
            };
        }
        return a + b;
    }
    // Make a try_it function that calls the new add
    // function incorrectly.
    
    var try_it = function (  ) {    try {
            add("seven");
        } catch (e) {
            document.writeln(e.name + ': ' + e.message);
        }
    }
    
    try_it(  );
    

Augmenting Types

Function.prototype.method = function (name, func) {
    this.prototype[name] = func;
    return this;
};
Number.method('integer', function (  ) {
    return Math[this < 0 ? 'ceil' : 'floor'](this);
});
document.writeln((-10 / 3).integer(  ));  // −3

Recursion

  • Recursion. JS compiler does not have optimization for tail recursion.
    // Define a walk_the_DOM function that visits every
    // node of the tree in HTML source order, starting
    // from some given node. It invokes a function,
    // passing it each node in turn. walk_the_DOM calls
    // itself to process each of the child nodes.
    var walk_the_DOM = function walk(node, func) {
        func(node);
        node = node.firstChild;
        while (node) {
            walk(node, func);
            node = node.nextSibling;
        }
    };
    
    // Define a getElementsByAttribute function. It
    // takes an attribute name string and an optional
    // matching value. It calls walk_the_DOM, passing it a
    // function that looks for an attribute name in the
    // node. The matching nodes are accumulated in a
    // results array.
    
    var getElementsByAttribute = function (att, value) {
        var results = [];
    
        walk_the_DOM(document.body, function (node) {
            var actual = node.nodeType === 1 && node.getAttribute(att);
            if (typeof actual === 'string' &&
                    (actual === value || typeof value !== 'string')) {
                results.push(node);
            }
        });
    
        return results;
    };
    

Scope and Closure

JavaScript does have function scope. That means that the parameters and variables defined in a function are not visible outside of the function, and that a variable defined anywhere within a function is visible everywhere within the function. ... it is best to declare all of the variables used in a function at the top of the function body.

var foo = function (  ) {
    var a = 3, b = 5;

    var bar = function (  ) {
        var b = 7, c = 11;

// At this point, a is 3, b is 7, and c is 11

        a += b + c;

// At this point, a is 21, b is 7, and c is 11

    };

// At this point, a is 3, b is 5, and c is not defined

    bar(  );

// At this point, a is 21, b is 5

};

... the function has access to the context in which it was created. This is called closure.

var myObject = (function () {
    var value = 0;

    return {
        increment: function (inc) {
            value += typeof inc === 'number
            value += typeof inc === 'number' ? inc : 1;
        },
        getValue: function (  ) {
            return value;
        }
    };
}());

Avoid creating functions within a loop. It can be wasteful computationally,and it can cause confusion,

// BAD EXAMPLE
// Make a function that assigns event handler functions to an array of nodes the wrong way.
// When you click on a node, an alert box is supposed to display the ordinal of the node.
// But it always displays the number of nodes instead.
var add_the_handlers = function (nodes) {
    var i;
    for (i = 0; i < nodes.length; i += 1) {
        nodes[i].onclick = function (e) {
            alert(i);
        };
    }
};

// BETTER EXAMPLE
// Make a function that assigns event handler functions to an array of nodes.
// When you click on a node, an alert box will display the ordinal of the node.
var add_the_handlers = function (nodes) {
    var helper = function (i) {
       return function (e) {
          alert(i);
       };
    };
    var i;
    for (i = 0; i < nodes.length; i += 1) {
        modes[i].onclick = helper(i); // Note that here we use a function call.
    }
};

Memoization

var fibonacci = (function (  ) {
    var memo = [0, 1];
    var fib = function (n) {
        var result = memo[n];
        if (typeof result !== 'number') {
            result = fib(n − 1) + fib(n − 2);
            memo[n] = result;
        }
        return result;
    };
    return fib;
}( ));

var memoizer = function (memo, formula) {
    var recur = function (n) {
        var result = memo[n];
        if (typeof result !== 'number') {
            result = formula(recur, n);
            memo[n] = result;
        }
        return result;
    };
    return recur;
};

var fibonacci = memoizer([0, 1], function (recur, n) {
    return recur(n − 1) + recur(n − 2);
});

Reserved words

To use reserved words as property names, the bracket notation must be use.

abstract boolean break byte case catch char class const continue
debugger default delete do double else enum export extends false final
finally float for
function goto if implements import in instanceof int interface long native new null
package private protected public return short static super switch synchronized this
throw throws transient true try typeof var volatile void while with

References

Comment