JavaScript: The Good Parts

 •  Filed under book

In this book Douglas Crockford distills and demonstrates the good parts of the JavaScript programming language and also tells us which parts of the language to steer clear of.

We find the good parts in the products that we use. We value simplicity, and when simplicity isn't offered to us, we make it ourselves. My microwave oven has tons of features, but the only ones I use are cook and the clock. And setting the clock is a struggle. We cope with the complexity of feature-driven design by finding and sticking with the good parts.

My notes of JavaScript: The Good Parts


  • The value NaN is a number value that is the result of an operation that cannot produce a normal result. NaN is not equal to any value, including itself. You can detect NaN with the isNaN(number) function.
  • The \u convention allows for specifying character code points numerically.
    "A" === "\u0041"
  • (Talking about the for-in statement)
    It is usually necessary to test object.hasOwnProperty(variable), to determine whether the propety name is truly a member of the object or was found instead on the prototype chain.
  • 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.
  • JavaScript is a prototypal inheritance language. That means that objects can inherit properties directly from other objects. The language is class-free.
  • If a function is invoked with the new prefix, then a new object will be created with a hidden link to the value of the function's prototype member, and this will be bound to that new object.
  • When a function is not the property of an object, then it is invoked as a function:
    var sum = add(3,4)
    When a function is invoked with this pattern thisis bound to the global object. This was a mistake in the design of the language. Had the language been designed correctly, when the inner function is invoked, this would still be bound to the this variable of the outer function.
  • Because of a design error arguments is not really an array. It is an array-like object. arguments has a length property, but it lacks all of the array methods.
  • Some languages offer the tail recursion optimization. This means that if a function returns the result of invoking itself recursively, then the invocation is replaced with a loop, which can significantly speed things up. Unfortunately, JavaScript does not currently provide tail recursion optimization. Functions that recurse very deeply can fail by exhausting the return stack.
  • Most languages with C syntax have block scope. All variables defined in a block ( a list of statements wrapped with curly braces) are not visible from outside of the block. The variables defined in a block can be released when execution of the block is finished. This is a good thing.
    Unfortunately, JavaScript does not have block scope even though its block syntax suggests that it does. This confusion can be a source of errors.
    (As of ES6, const and let variables are block-level scoped. var variables still are not.)
  • It is important to understand that the inner function has access to the actual variables of the outer functions and not copies in order to avoid the following problem:
//BAD EXAMPLE
// 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) {
        nodes[i].onclick = helper(i);
    }
};
  • The general pattern of a module is a function that defines private variables and functions; creates privileged functions which, through closure, will have access to the private variables and functions; and that returns the privileged functions or stores them in an accessible place.
  • Every function gets a prototype object because the language does not provide a way of determining which functions are intended to be used as constructors. The constructor property is not useful. It is the prototype object that is important.

Constructing an object with the new keyword from a function is a 3 step process.

Example:

 function Mammal(name){
   this.name = name;
 }

  const mammal = new Mammal();
  1. Create a new empty object, setting its proto property to the .prototype property of the function invoked with the new keyword. (Functions have a .prototype property, objects do not)

    (An empty object is created which looks like {}. The proto property of the empty object is set to an empty object which has a proto and a constructor property, the value of which is the Mammal function )

  2. Execute the code inside the constructor, setting the instance variables on the newly created object, if any, in the process.
    (the only code inside the Mammal constructor sets the name instance v)

  3. Return the newly created object.

  • Inheriting in JS is setting the prototype property to an INSTANCE of the base class.
  • Object specification
    It sometimes happens that a constructor is given a very large number of parameters.
    This can be troublesome because it can be very difficult to remember the order of the arguments. In such cases, it can be much friendlier if we write the constructor to accept a single object specifier instead. That object contains the specification of the object to be constructed. So instead of:
var myObject = maker(f, l, m, c, s);
//we can write

var myObject = maker({
    first: f,
    last: l,
    middle: m,
    state: s,
    city: c
});

  • Instead, JavaScript provides an object that has some array-like characteristics. It converts array subscripts into strings that are used to make properties. It is significantly slower than a real array, but it can be more convenient to use. Retrieval and updating of properties work the same as with objects, except that there is a special trick with integer property names. Arrays have their own literal format. Arrays also have a much more useful set of built-in methods.
  • 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   //100001
//myArray contains one property.

(JS arrays are sparse arrays in most implementations)

  • The [] postfix subscript operator converts its expression to a string using the expression's toString method if it has one. That string will be used as the property name. If the string looks like a positive integer that is greater than or equal to the array's current length and is less than 4,294,967,295, thenthe length of the array is set to the new subscript plus one.
    [ADD ALL THAT FOLLOWS ON THE NEXT PAGE]
  • Since JavaScript's arrays are really objects, the for-in statement can be used to iterate over all of the properties of an array. Unfortunately, for-in makes no guarantee about the order of the properties, and most array applications expect the elements to be produced in numerical order. Also, there is still the problem with unexptected properties being dredged up from the prototype chain.
    (With ES6 you can use for-of to iterate arrays in numerical order)
  • Your comparison function should take two parameters and return 0 if the two parameters are equal, a negative number if the first parameter should come first, and a positive number if the second parameter should come first.
    (The function basically answers the question: 'Should I swap these two params?')
  • The search method is like the indexOf method, except that it takes a regular expression object instead of a string. It returns the position of the first character of the first match, if there is one, or -1 if the search fails. The g flag is ignored. There is no position parameter.
  • Global Variables
    The problem with JavaScript isn't just that it allows them, it requires them. JavaScript does not have a linker. All compilation units are loaded into a common global object.
  • In most languages, it is generally best to declare variables at the site of first use. That turns out the be a bad practice in JavaScript because it does not have block scope. It is better to declare all variables at the top of each function.
  • Implementations disagree on the type of regular expression objects. Some implementations report that: typeof \a\ is 'object', and others say that it is 'function'. It might have been more useful to report 'rexexp', but the standard does not allow that.
  • Binary floating-point numbers are inept at handling decimal fractions, so 0.1 + 0.2 is not equal to 0.3. This is the most frequently reported bug in JavaScript, and it is an intentional consequence of having adopted the IEEE Standard for Binary Floating-Point Arithmetic (IEEE 754). This standard is well-suited for many applications, but it violates most of the things you learned about numbers in middle school.
    (Note, that this is not specific to JavaScript.)
  • JavaScript has a surprisingly large set of falsy values:
Value Type
0 Number
NaN (not a number) Number
''(empty String) String
false Boolean
null Object
undefined Undefined
  • undefined and NaN are not constants. They are global variables, and you can change their values. that shoul dnot be possible, and yet it is. Don't do this.
    (Really, don't!)
  • The eval function also compromises the security of your application because it grants too much authority to the eval'd text. And it compromises the performance of the language as a whole in the same way that the with statement does.
  • The Function constructor is another form of eval, and should similarly be avoided.
  • The browser provides setTimeout and setInterval functions that can take string arguments or function arguments. When given string arguments, setTimeout and setInterval act as eval. the string argument form also should be avoided.
  • In my own practice, I observed that when I used ++ and --, my code tended to be too tight, too cryptic. So, as a matter of discipline, I don't use them anymore.
  • In many languages, void is a type that has no values. In JavaScript, void is an operator that takes an operand and returns undefined. this is not useful, and it is very confusing. Avoid void.