This post is inspired and quotes from the book JavaScript: The Good Parts by Douglas Crockford. Douglas is well known in the community around JavaScript. Douglas, for example, created a tool JSLint.
With my bad English I first understood the title "The Good Parts" in the sense of JavaScript is good to be friends. After reading the introduction, I realized that it was something else.
“Most programming languages contain good parts and bad parts. I discovered that I could be a better programmer by using only the good parts and avoiding the bad parts. After all, how can you build something good out of bad parts?”
Numbers
The global NaN property is a number value representing Not-A-Number that is the result of an operation that cannot produce a normal result. NaN is not equal to any value, including itself.
Testing against NaN
Equality operator (== and ===) cannot be used to test a value against NaN. Use Number.isNaN() or isNaN() instead.
NaN === NaN; // false
Number.NaN === NaN; // false
isNaN(NaN); // true
isNaN(Number.NaN); // true
The value Infinity represents all values greater than 1.79769313486231570e+308.
String
The \u convention allows for specifying character code points numerically.
"A" === "\u0041"
Boolean
Here are the falsy values:
• false
• null
• undefined
• The empty string ''
• The number 0
• The number NaN
All other values are truthy, including true, the string 'false', and all objects.
Statements
It is usually necessary to test object.hasOwnProperty(variable) to determine whether the property name is truly a member of the object or was found instead on the prototype chain.
for (myvar in obj) {
if (obj.hasOwnProperty(myvar)) {
…
}
}
Expressions
The values produced by typeof are 'number', 'string', 'boolean', 'undefined', 'function', and 'object'. If the operand is an array or null, then the result is 'object', which is wrong.
Object Literals
The quotes around a property’s name in an object literal are optional if the name would be a legal JavaScript name and not a reserved word. So quotes are required around "first-name", but are optional around first_name.
var person = {
"first-name": "Jerome",
last_name: "Howard"
};
Retrieval
The undefined value is produced if an attempt is made to retrieve a nonexistent member:
stooge["middle-name"] // undefined
flight.status // undefined
stooge["FIRST-NAME"] // undefined
The || operator can be used to fill in default values:
var middle = stooge["middle-name"] || "(none)";
var status = flight.status || "unknown";
Attempting to retrieve values from undefined will throw a TypeError exception. This can be guarded against with the && operator:
flight.equipment // undefined
flight.equipment.model // throw "TypeError"
flight.equipment && flight.equipment.model // undefined
Enumeration
The for in statement can loop over all of the property names in an object. The enumeration will include all of the properties including functions and prototype properties that you might not be interested in so it is necessary to filter out the values you don’t want. The most common filters are the hasOwnProperty method and using typeof to exclude functions:
var name;
for (name in another_stooge) {
if (typeof another_stooge[name] !== 'function') {
document.writeln(name + ': ' + another_stooge[name]);
}
}
If you want to assure that the properties appear in a particular order, it is best to avoid the for in statement entirely and instead make an array containing the names of the properties in the correct order:
var i;
var properties = [
'first-name',
'middle-name',
'last-name',
'profession'
];
for (i = 0; i < properties.length; i += 1) {
document.writeln(properties[i] + ': ' +
another_stooge[properties[i]]);
}
Delete
The delete operator can be used to remove a property from an object. It will remove a property from the object if it has one. It will not touch any of the objects in the prototype linkage.
another_stooge.nickname // 'Moe'
// Remove nickname from another_stooge, revealing the nickname of the prototype.
delete another_stooge.nickname;
another_stooge.nickname // 'Curly'
Function Object
Functions in JavaScript are objects. Objects are collections of name/value pairs having a hidden link to a prototype object. Objects produced from object literals are linked to Object.prototype. Function objects are linked to Function.prototype.
Arguments
A bonus parameter that is available to functions when they are invoked is the arguments array.
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
Exception
var add = function (a, b) {
if (typeof a !== 'number' || typeof b !== 'number') {
throw {
name: 'TypeError',
message: 'add needs numbers'
};
}
return a + b;
}
var try_it = function ( ) {
try {
add("seven");
} catch (e) {
document.writeln(e.name + ': ' + e.message);
}
}
Augment
For example, by augmenting Function.prototype, we can make a method available to
all functions:
Function.prototype.method = function (name, func) {
this.prototype[name] = func;
return this;
};
Curry
Functions are values, and we can manipulate function values in interesting ways. Currying allows us to produce a new function by combining a function and an argument:
var add1 = add.curry(1);
document.writeln(add1(6)); // 7
Memoization
We will keep our memoized results in a memo array that we can hide in a closure. When our function is called, it first looks to see if it already knows the result. If it does, it can immediately return it:
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;
}( ));
Array
The length property is the largest integer property name in the array plus one. This is not necessarily the number of properties in the array:
var myArray = [];
myArray.length // 0
myArray[1000000] = true;
myArray.length // 1000001
// myArray contains one property.
delete
Since JavaScript’s arrays are really objects, the delete operator can be used to remove
elements from an array:
delete numbers[2];
// numbers is ['zero', 'one', undefined, 'shi', 'go']
numbers.splice(2, 1);
// numbers is ['zero', 'one', 'shi', 'go']
isArray
JavaScript does not have a good mechanism for distinguishing between arrays and
objects. We can work around that deficiency by defining our own is_array function:
var is_array = function (value) {
return value && typeof value === 'object' && value.constructor === Array;
};
Unfortunately, it fails to identify arrays that were constructed in a different window or frame. If we want to accurately detect those foreign arrays, we have to work a little harder:
var is_array = function (value) {
return Object.prototype.toString.apply(value) === '[object Array]';
};
Bonus - Shuffle array
yourArray.sort(function() { return 0.5 - Math.random() });
Bad parts
JavaScript has two sets of equality operators: === and !==, and their evil twins == and !=. The good ones work the way you would expect. If the two operands are of the same type and have the same value, then === produces true and !== produces false. The evil twins do the right thing when the operands are of the same type, but if they are of different types, they attempt to coerce the values. The rules by which they do that are complicated and unmemorable. These are some of the interesting cases:
'' == '0' // false
0 == '' // true
0 == '0' // true
false == 'false' // false
false == '0' // true
false == undefined // false
false == null // false
null == undefined // true
' \t\r\n ' == 0 // true
Conclusion
The book has about 150 pages and is a fast reading. Perhaps you encounter language features that are at least strange and you will be able to avoid such parts.
It might interest you
- JavaScript Garden - documentation about the most quirky parts of the JavaScript programming language.
Reference
- CROCKFORD, Douglas. JavaScript: the good parts [online]. 1st ed. Sebastopol: O´Reilly, 2008, xiii, 153 s. [cit. 2014-01-04]. ISBN 978-0-596-51774-8.
No comments:
Post a Comment