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 produceobject
)- 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 againstundefined
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'
- The Method invocation pattern
- 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