dwyl / Javascript-the-Good-Parts-notes

:book: Notes on the seminal "JavaScript the Good Parts: by Douglas Crockford
1.19k stars 230 forks source link

Notes on Douglas Crockford's Javascript the Good Parts :rocket:

These are notes on the book Javascript the Good Parts which was published in 2008, before es6. There have not been any revised editions of the book published at the time of writing, see https://www.github.com/dwyl/Javascript-the-Good-Parts-notes/issues/33 for the ongoing conversation on this.

This book calls out the best parts of Javascript and tells you what to avoid (the 'bad parts'). It's about making sure you know the really important parts of the language and create good habits instead of having to break bad ones down the line.

The idea of these notes is to collect the excellent information from an already dense book into note form, translating these ideas into 'plain English' where possible and adding explanations throughout where the book might not make things obvious as it assumes prior knowledge.

You can buy the book or follow Douglas Crockford on Github.

contributions welcome Please don't hesitate to submit PRs to improve these notes wherever you feel something can be clarified, improved on or have an example added to it.

Table of Contents

Chapter 1 - Good Parts

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?

The best parts of Javascript include:

  • functions
  • loose typing (variables are declared as variables, without a type)
  • dynamic objects
  • object literal notation (where you can create an object already with a list of key/value pairs inside curly braces)

The worst parts include global variables - there is a common global object namespace where they're all lumped together and they're essential to the language.

Javascript has a class free object makeup, relying instead on objects inheriting properties directly from other objects - this is prototypal inheritance.

Chapter 2 - Grammar

Always use // for comments, even multi-line ones to avoid having to escape /* characters.

Numbers

  • There is a single, 64-bit floating point number type.
  • NaN (Not-a-Number) is not equal to any value (including itself) and is essentially an illegal number value, but typeOf(NaN)===number is true
  • Use isNaN(number) to check for NaNs

Number methods are discussed in Chapter 8.

Strings

Single quotes are often used to define a String in JavaScript, but if a person's name has an apostrophe (and the developer does not know the difference between an apostrophe and single quote) it is useful to "escape" the apostrophe character:

var name = 'Patrick O\'Brian'; // using a backslash in front of the apostrophe
console.log('name:', name); // name: Patrick O'Brian

further reading: https://webdesignledger.com/common-typography-mistakes-apostrophes-versus-quotation-marks

BackSlashExample

String methods are discussed in Chapter 8.

Statements

Expressions

Literals

Functions

Chapter 3 - Objects

Javascript simple types:

  • numbers (has object-like methods but they are immutable)
  • strings (has object-like methods but they are immutable)
  • booleans (has object-like methods but they are immutable)
  • null
  • undefined

All other values are objects including arrays and functions.

Objects are class free, can contain other objects and can inherit properties from their prototypes (which can reduce object initialisation time and memory consumption).

Object Literals

  • An object literal is zero or more comma-separated name/value pairs surrounded by curly braces {}

var empty_object = {};

var today = {
    day: "Wednesday",
    month: "April",
    year: 2014,

    weather: { //objects can contain nested objects like this one
        morning: "sunny",
        afternoon: "cloudy"
    }
}

Retrieval

  • Can be done with either dot notation today.weather.morning or with square brackets today['month']
  • Or operand (||) can be used to fill in default values for nonexistent data to prevent and undefined error: var weath = today.weather.evening || "unknown"

Update

  • Assigning a property value to an object overwrites any existing property values with that property name

Reference

  • Objects refer to each other, they don't hold duplicate copies of data

Prototype

  • Every object has a prototype object from which it inherits properties
  • Object.prototype comes standard with Javascript and is almost like a 'root parent' Object prototype diagram
  • The Object.create method is now available in ES5 (but the method is in the book if required for older versions)
  • If an object does not have a property you ask it for, it will keep looking up the prototype chain until it finds it
    • If the property does note exist anywhere in the chain, it will return undefined
  • A new property is immediately visible to all of the objects below it in the chain once created

More details in Chapter 6

Reflection

Enumeration

Delete

Global Abatement

var MYAPP = {}

MYAPP.today = {
    day: "Wednesday",
    month: "April",
    year: 2014,

    weather: { //objects can contain nested objects like this one
        morning: "sunny",
        afternoon: "cloudy"
    }
}
//Making sure all other variables (like today) are contained within this one global variable (MYAPP) means none of them have global scope and therefore the risk of naming conflicts, etc in your application is reduced

Chapter 4 - Functions

The best thing about JavaScript is its implementation of functions.

Function Objects

  • Functions are objects linked to function.prototype (which is linked to Object.prototype).
  • As well as usual object behaviour, they can be invoked.

Function Literal

  • A function literal has 4 parts:
    • The (reserved) word function itself
    • An optional name (un-named functions are considered anonymous functions)
    • Comma-seperated parameters of the function, in parentheses - (parameters)
    • Set of statements in curly brackets to be carried out when the function is invoked - {statements}
//Format of a function
function name (parameterA, parameterB){
    statements;
}

  • Functions can be nested within functions and the inner function can access all the parameters of the outer function as well as its own

Invocation

  • Stops the current function from running and tells the function you have invoked both to start and to use the arguments (values in parentheses) you have passed it in the invocation function (parameters)

    • If arguments > number of arguments expected, the extra values will be ignored
    • If arguments < number of arguments expected, the function will assume undefined in place of the missing arguments
    • No error is thrown
  • Note: The difference between an argument and a parameter is that a parameter is usually what is used in the function literal, when you're setting up the function (almost like the placeholder for the actual values that the function will use when it is active) and an argument is usually the value passed to a function at the time it is invoked

  • Parameters this and arguments are also passed to the function when it is invoked, but their value depends on how the function is invoked

Method Invocation Pattern

  • When a function is stored as the property of the object (invoked with a dot . expression) it is called on and is called a method
    myObject.incrementFunction();
  • The method is bound to the object and therefore can use this to retrieve or update values from the object
  • These methods are highly reusable
  • Because their object context comes from this they are considered public methods

Function Invocation Pattern

Workaround: Artificially create a new this:

myObject.double = function() {
    //in the book, the var here is called `that` but name changed for clarity
    var globalScopeThis = this; //workaround

    var innerFunc = function() {
        globalScopeThis.value = add(globalScopeThis.value, globalScopeThis.value);
    };

    innerFunc(); //invoke innerFunc as function
};

myObject.double();
console.log(myObject.value);

Constructor Invocation Pattern

//create a function Quo that takes a string - Quo will be our prototype function as we will see
var Quo = function (string){
    this.status = string;
}

//Now create a get_status method for Quo - this will be a public method
Quo.prototype.get_status = function () {
    return this.status;
}

//create a new instance of Quo using the prefix NEW
var myQuo = new Quo("happy");

//because of the use of the new prefix, myQuo is an instance of Quo which means it can access the public method get_status from it's prototype
document.writeIn(myQuo.get_status());     //returns 'happy'

Apply Invocation Pattern

var array = [5, 2]    //will be the parameters for our function
var sum = add.apply(null, array);     //value of 'this' is null and value of sum is 7 as the 'apply' method passes 5 and 2 to the 'add' method

Arguments

Return

Exceptions

//When you use the function later on, add a try block with a catch clause to catch the exception object
var try_it = function () {
    try{
        add("seven");   //will throw an exception as it is not a number
    }
    catch (e) {
        document.writeIn(e.name + ':' + e.message);
    }
}

try_it();    //you could rewrite this function so the argument is passed in here where it is invoked

Augmenting Types

//Makes a method available to all functions, ONLY when it definitely does not already exist

Function.prototype.method (methodName, func) {
    if (!this.prototype[methodName]){
        this.prototype[methodName] = func;
        return this;
    }
};

Recursion

var variable = function functionName (parameters){
    //wrap the statements to be executed and the recursive call in a loop statement so it doesn't recurse forever
    //statements to be executed in the function;
    functionName(arguments);
};

functionName (initialArguments); //initial call to the function

Scope

Closure

Callbacks

Module

Cascade

Curry

//set up a simple function that we will customise with curry
var add = function (a,b){
    return a + b;
}

var addTen = add.curry(10);      //passes 10 as the first argument to the add() function
addTen(20);                     //The use of the curry method in addTen means addTen === add(10, 20);

Memoization

Chapter 5 - Inheritance

Javascript is a prototypal language, which means that objects inherit directly from other objects

Main benefit of inheritance is code reuse - you only have to specify differences.

Javascript can mimic classical inheritance but has a much richer set of code reuse patterns

  • This chapter looks at the more straightforward patterns but it is always best to keep it simple

Pseudoclassical

  • The pseudoclassical code reuse pattern essentially has constructor functions (functions invoked using the new prefix) work like classes to mimic the classical structure

    • All properties are public
    • If you forget to use the new prefix, this is not bound to the new object - it is instead bound to the global object and you'll be unwittingly altering these instead!
  • There is no need to use it, there are better code reuse patterns in JavaScript

Object Specifiers

Rather than: var myObject = maker (f, l, m, c, s) which has too many parameters to remember in the right order, use an object specifier:

var myObject = maker ({      //note curly braces
    first: f,
    last: l,
    state: s,
    city: c
    }
;)

to contain them. They can now be listed in any order

Also useful to pass object specifiers to JSON (see Appendix E notes)

Prototypal

Functional

var mammal = function (spec) {
    var that = {};    //that is a new object which is basically a container of 'secrets' shared to the rest of the inheritance chain

    that.get_name = function () {
        return spec.name;
    };

    that.says = function () {
        return spec.saying || '';  //returns an empty string if no 'saying' argument is passed through the spec object when calling mammal
    };
    return that;     //returns the object that contains the now private properties and methods (under functional scope)
}

var myMammal = mammal({name: 'Herb'});

Creating an object 'cat' can now inherit from the mammal constructor and only pay attention to the differences between it and mammal:

var cat = function (spec) {
    spec.saying = spec.saying || 'meow';   //if spec.saying doesn't already exists, make it 'meow'
    var that = mammal(spec);      //here the object 'container of secrets' is set up inheriting from mammal already

    //functions and property augmentations happen here

    return that;      //as above
}

Parts

Chapter 6 - Arrays

Javascript only has array-like objects which are slower than 'real' arrays.

Retrieval and updating of properties works the same as with an object except with integer property names.

Arrays have their own literal format and their own set of methods (Chapter 8 - Methods).

Array Literals

Length

Delete

Enumeration

Confusion

The rule is simple: when the property names [keys] are small sequential integers, you should use an array. Otherwise, use an object.

Methods

Dimensions

Chapter 7 - Regular Expressions

A regular expression is the specification of the syntax of a simple language

Used with regexp.exec, regexp.test, string.match, string.replace, string.search and string.split to interact with string (more in Chapter 8 - Methods)

Quite convoluted and difficult to read as they do not allow comments or whitespace so a JavaScript regular expression must be on a single line

An Example

/ˆ(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([ˆ?#]*))?(?:\?([ˆ#]*))?(?:#(.*))?$/

Breaking it down one portion (factor) at a time:

Another example /ˆ-?\d+(?:\.\d*)?(?:e[+\-]?\d+)?$/i;

Most of this we have seen before but here are the new bits:

Construction

3 flags exist in regular expressions: i means insensitive - ignore the character case, g means global - to match multiple items and m means multiline - where ˆ and $ can match line-ending characters

Two ways to build a regular expression:

  1. Regular Expression literals as per the examples above start and end with a slash /
    • Here the flags are appended after the final slash, for example /i
    • Be careful: RegExp objects made by regular expression literals share a single instance
  2. Use RegExp constructor
    • The first parameter is the string to be made into a RegExp object, the second is the flag
    • Useful when all information for creating the regular expression is not available at time of programming
    • Backslashes mean something in the constructor, so these must be doubled and quotes must be escaped
//example creating a regular expression object that matches a JavaScript string

var my_regexp = new RegExp("'(?:\\\\.|[ˆ\\\\\\'])*'", 'g');

Elements

Regexp Choice

| provides a match if any of the sequences provided match.

In "into".match(/in|int/);, the in will be a match so it doesn't even look at the int.

Regexp Sequence

A regexp sequence is made up of one or more regexp factors. If there are no quantifiers after the factor (like ?, * or +), the factor will be matched one time.

Regexp Factor

A regexp factor can be a character, a parenthesized group, a character class, or an escape sequence.

It's essentially a portion of the full RegExp, like what we broke down the regexp above into.

Regexp Escape

As well as escaping special characters in regexp factors, the backslash has additional uses:

*\b is a bad part. It was supposed to be a word-boundary anchor but is useless for multilingual applications

Regexp Group

Four kinds of groups:

Regexp Class

  • Conveniently and easily specifies one of a set of characters using square brackets [], for example vowels: [aeiou]
  • Can shorten specification of all 32 ASCII special characters to [!-\/:-@[-`{-˜] (note that the ` in this piece of code is a back-tick)
  • Also allows ˆ as the first character after the opening [ to mean NOT the characters in the character set

Regexp Class Escape

There are specific characters that must be escaped in a character class: - / [ \ ] ˆ

Regexp Quantifier

A quantifier at the end of a factor indicates how many times the factor should be matched

  • A number in curly braces means the factor should match that many times, so /o{3} matches ooo
  • Two comma-seperated numbers in curly braces provide the range of times a factor should match, so {3,5} indicates it will match 3, 4 or 5 times
  • Zero or one times (same thing as saying something is optional) can be ? or {0,1}
  • Zero or more times can be * or {0,}
  • One or more times can be + or {1,}

Prefer to use 'zero or more' or 'one or more' matching over the 'zero or one' matching - i.e. prefer greedy matching over lazy matching

Chapter 8 - Methods

Arrays

array.concat(item...)

Produces new array copying the original array with the items appended to it (does not modify the original array like array.push(item) does). If the item is an array, its elements are appended.

array.join(separator)

Creates a string of all the array's elements, separated by the separator. Use an empty string separator ('') to join without separation.

array.pop()

Removes last element of array. Returns undefined for empty arrays.

array.push(item...)

Modifies the array, appending items onto the end. Returns the new length of the array.

array.reverse()

Modifies the array by reversing the order of the elements.

array.shift()

Removes the first element of the array (does not leave a hole in the array - same effect as using the .splice(a,b) method) and returns that first element.

array.slice(start, end)

Different to splice.

'slice' creates a new array, copying from the start element and stopping at the element before the end value given. If no end is given, default is array.length.

Negative values for start and end will have array.length added to them and if start>end, it will return an empty array.

array.sort(comparefn)

JavaScript has a sort() method which was created only to compare strings and therefore sorts numbers incorrectly (it will sort them as 1, 15, 2, 23, 54 for example). Therefore, we have to write a comparison function which returns 0 if the two elements you are comparing are equal, a positive number if the first element should come first and a negative number if the second element should come first. Then pass this comparison function to sort() as a parameter to allow it to sort array elements intelligently.

Page 80-82 in the book takes you through various iterations of the comparison functions - for numbers, simple strings, objects and objects with multiple keys (for example if you want to sort objects by first and last names). These should be taken from the book when required.

array.splice(start, deleteCount, item...)

Removes elements from the array making sure there are no holes left in the array. It is most popularly used for deleting elements from an array.

It removes the deleteCount number of elements from the array starting from the start position. If there are item parameters passed to it, it will replace the deleted elements in the array with the items.

It returns an array containing the deleted elements.

array.unshift(item...)

Works like push but adds items to the front of the array instead of the end. Returns the new length of the array.

Function

function.apply(thisArg, [argArray])

The apply method invokes a function, passing in the object that will be bound to this and optional array of arguments.

Number

number.toExponentional(fractionDigits)

Converts number to a string in exponential form (e.g. 3.14e+0). fractionDigits (from 0 to 20) gives the number of decimal places.

number.toFixed(fractionDigits)

Converts number to a string in decimal form (e.g. 3.1415927). fractionDigits (from 0 to 20) gives the number of decimal places.

number.toPrecision(precision)

Converts number to a string in decimal form (e.g. 3.1415927). The difference from toFixed is that precision (from 0 to 21) gives the number of total digits.

number.toString(radix)

Converts number to a string. radix is an optional parameter between 2 and 36 and gives the base. The default radix is 10.

Object

object.hasOwnProperty(name)

Does not look at the property chain. Returns true if the object contains the property name.

RegExp

regexp.exec(string)

Most powerful (and slowest) regexp method.

Checks the string against the regexp (starting at position 0) and returns an array containing the matches. The regexp is set up with various capturing groups and these determine the elements that go in the array:

  • the 0 element of the array will contain the part of string that matched the regexp
  • element 1 of the array will contain the text captured by the first capturing group in regexp
  • element 2 of the array will contain the text captured by the second capturing group in regexp and so on
  • if the match fails, it returns null

If the regexp contains a g flag (e.g. var regexp = /[ˆ<>]+|<(\/?)([A-Za-z]+)([ˆ<>]*)>/g;), there is a lot more to look out for:

  • Searching begins at regexp.lastIndex (initially zero)
  • If a match is found, lastIndex becomes the position of the first character of the match
  • If no match is found, lastIndex is reset to zero
  • If searching for multiple occurrences of a pattern by calling exec in a loop, ensure you reset lastIndex when exiting the loop and remember ˆ only matches when lastIndex is equal to zero

Example on page 87 of the book is worth reading to improve understanding.

regexp.test(string)

Simplest (and fastest) regexp method.

If regexp matches the string it returns true. Otherwise it returns false. Do not use the g flag with this method.

String

string.charAt(pos)

Returns character at position pos in the string starting from 0. If pos is less than zero or bigger than the string itself it return an empty string.

string.charCodeAt(pos)

Same as charAt except it returns the integer that represents the code point value of the character at position pos. Returns NaN if string.length < pos < 0.

string.concat(string...)

Creates new string concatenating various strings. + tends to be used instead of this method (e.g. var cat = 'c'+'a'+'t';)

string.indexOf(searchString, position)

Searches for searchString within string starting at position position (an optional parameter). If position is not provided, search starts at the beginning of the string. Returns the integer position of the first matched character or -1 if no match is found.

string.lastIndexOf(searchString, position)

Same as indexOf but searches from the end of the string instead of the beginning.

string.localeCompare(that)

Compares string to that parameter and returns:

  • 0 if string === that
  • -1 if string < that

NB. 'a' < 'A', comparison is not just in length.

string.match(regexp)

Works the same way as regexp.exec(string) if there is no g flag in the regexp.

If there is a g flag in teh regexp, it produces an array of the matches but excludes the capturing groups

string.replace(searchValue, replaceValue)

Searches for the searchValue in string and replaces it with the replaceValue.

If searchValue is a:

  • string, only its first occurrence will be replaced with the replaceValue
  • regexp with a g flag, all occurrences will be replaced with the replaceValue; otherwise, only the first occurrence will be replaced

If replaceValue is a:

  • string, a $ value has a special meaning when used in the replaceValue that conveys what to replace - see table on page 90 for possible variations on $
  • function, it is called for each match and the string result of the function is used as the replacement text
    • string result of the first call will replace capture group 1 of the string and so on

string.search(regexp)

Similar to .indexOf(string) but takes a regexp instead of a string, returning the position of the first match (or -1 if there is no match). The g flag is ignored.

string.slice(start, end)

Creates a new string by copying the characters from the start position to the character before the end position in string.

The end parameter is optional and defaults to string.length. If either parameter is negative, string.length is added to it.

string.split(separator, limit)

Creates an array of strings by splitting apart string at the points where the separator appears (e.g. if the separator is '.', ab.cd' becomes ['ab', 'cd']).

  • If separator is an empty string, an array of single characters is produced.
  • limit is optional and determines how many pieces are to be split off from the original string.
  • The separator can be a regexp but
    • text from capturing groups within the regexp will be included in the split - e.g. in var e = text.split(/\s*(,)\s*/); the commas (,) will each be included as a separate element in the resulting array
    • some systems ignore empty strings when the separator is a regexp

string.substring(start, end)

No reason to use, use slice instead.

string.toLocaleLowerCase()

Produces a new string converted to lower case, using the rules for the particular locale (geography).

string.toLocaleUpperCase()

Produces a new string converted to upper case, using the rules for the particular locale (geography).

string.toLowerCase()

Produces a new string converted to lower case.

string.toUpperCase()

Produces a new string converted to upper case.

String.fromCharCode(char...)

Produces a new string from a series of numbers. var a = String.fromCharCode(67, 97, 116); //a === 'Cat' NB. You're calling the prototype here, not replacing 'String' with your own variable.

Chapter 9 - Style

JavaScripts's loose typing and excessive error tolerance provide little compile-time assurance of our programs' quality, so to compensate, we should code with strict discipline.

Chapter 10 - Beautiful Features

Each feature you add to something has a lot of different costs (documentation costs, specification, design, testing and development costs) and these are often not properly accounted for.

Features that offer value to a minority of users impose a cost on all users

We cope with the complexity of feature-driven design by finding and sticking with the good parts. For example, microwaves do a ton of different things, but most people just use one setting, the timer and the clock. So why not design with just the good parts?

Appendix A - the Awful Parts

Need to know what all the pitfalls are with these parts.

Global variables

These are variables that are visible throughout the code in any scope. They can be changed at any time by any part of the program which makes them unreliable in larger complex programs. This can also lead to naming conflicts which can cause your code to fail or you to accidentally overwrite a global variable.

Defined in three ways:

  • Using a var statement outside of any function; var foo = value;
  • By adding a property to the global object (container of all global variables), such as window in browsers; window.foo = value;
  • Using a variable without declaring it with var, which makes it an implied global; foo = value

Scope

Although JavaScript has block syntax (i.e. is written in blocks) like a lot of other programming languages, it has functional scope and not block scope.

Variables should all be declared at the top of the function and not littered throughout the block.

Semicolon Insertion

Attempts to correct faulty programs by automatically inserting semicolons. Do not depend on this as it can hide underlying issues.

Also ensure opening curly braces ({) are on the first line of a statement, otherwise semicolons will be erroneously inserted and cause problems:

//Ensure curly braces open on the first line of a statement
return {
    status: true    //for example
};
//instead of
return
{
    status:true
};

Reserved Words

Most JavaScript reserved words are not used in the language but cannot be used to name variables or parameters.

If used as the key in object literals, they must be quoted. For example object - {'case' : value}; or object['final'] = value; as case and final are both reserved words.

Unicode

JavaScript characters are 16 bits which only cover the original Unicode Basic Multilingual Place.

typeof

Watch out for:

  • typeof null which returns 'object' instead of 'null'
  • incorrect reporting on typeof regular expressions, with some implementations returning 'object' and some returning 'function'
  • arrays are objects in JavaScript so typeof array will return 'object'

All objects are truthy and null is falsy, so you can use the following to tell them apart:

if (my_value && typeof my_value === 'object') {
    //then my value is definitely an object or an array because not only is its 'typeof' an object but it's also truthy (first statement)
}

NaN

  • typeof NaN === 'number' even though it stands for not-a-number
  • If you have a chain of formulas that together produce a NaN then at least one of them will have generated NaN
  • Surprisingly NaN !=== NaN
  • isNaN(value) can be used to distinguish numbers from NaN

For numbers, best use your own isNumber formula:

var isNumber = function isNumber(value) {
    return typeof value === 'number' && isFinite(value);    //isFinite() rejects NaN and Infinity, but is only good for numbers, not strings
}

Phony Arrays

JavaScript doesn't have real arrays, it has array-like objects.

  • Good: No need to give them dimensions and don't generate out-of-bounds errors
  • Bad: Slower than 'real' arrays

To test if value is an array:

if (my_value && typeof my_value === 'object' && typeof my_value.length === 'number' &&
    !(my_value.propertyIsEnumerable('length'))) {
        //my_value is definitely an array!
}

The arguments array isn't an array, just an object with a length property.

Falsy Values

0, NaN, '', false, null and undefined are all falsy values, but they are not interchangeable. When testing for a missing member of an object for example, you need to use undefined and not null.

undefined and NaN are actually global variables instead of constants but don't change their values.

Object

JavaScript objects inherit members from the prototype chain so they are never truly empty.

To test for membership without prototype chain involvement, use the hasOwnProperty method or limit your results (for example, to specific types like number so you know you're not dragging in object members from up the prototype for example if that's what's causing the problem).

Appendix B - the Bad Parts

Avoid these altogether

  • == and !=: Don't function properly when result is false, use === or !== instead
  • with statement: Intended to provide a shortcut to properties of an object but results vary every time it is run
  • eval: Adds unnecessary complication and compromises the security of the application
    • Giving string arguments to setTimeout and setInterval should also be avoided as this makes them act like eval
  • continue statement: Forces a loop into its next iteration but the code is usually much improved when re-written without continue
  • switch fall through: In a switch statement, each case falls through to the next case unless you explicitly disrupt the flow, but using these intentional fall throughs makes the unintentional ones that are causing errors basically impossible to find
    • This is one of those parts of JavaScript that appears useful but you're better off avoiding because it's occasionally very dangerous
  • Block-less statements: Always use curly braces {} to block in statements so as to avoid misinterpretation and aid error finding
  • Bitwise operators: Shouldn't really be doing this kind of manipulations because they are quite slow in JavaScript, therefore there shouldn't be a need to use &, |, ˆ, ˜, >>, >>> or <<
    • This doesn't mean you can't use && for example
  • ++ and --: This one seems debatable to me; Douglas Crockford finds it makes his coding style much more cryptic and difficult to read (the book uses +=1 and -=1 instead)

The function statement vs the function expression: To use JavaScript well, important to understand that functions are values.

  • A function statement is shorthand for a var statement with a function value, so function foo() {} (a function statement) means pretty much the same as var foo = function foo(){}; (a function expression)
  • Logically, to write the language well you should define a function before using it, but in JavaScript, function statements (using just function foo(){}) are hoisted to the top of the scope in which they are defined - this encourages sloppy programming and should be avoided
  • function statements also don't function consistently in if statements
  • if you need to start a function expression with the word function, wrap it in parentheses (), or JavaScript assumes it's a function statement

Typed wrappers: Don't use new Boolean or new String or new Number, it's completely unnecessary. Also avoid new Object and new Array and use {} and [] instead.

new operator: Functions that are intended to be used with new (conventionally starting with a capital letter) should be avoided (don't define them) as they can cause all kinds of issues and complex bugs which are difficult to catch.

void: In JavaScript, this actually takes a value and returns undefined, which is hugely confusing and not helpful. Don't use it.

Appendix C - JSLint

JSLint is a code quality tool for JavaScript which checks your syntax.

Having read through this appendix (you can read more about JSLint here), I tend more towards JSHint, a fork of JSLint. It allows programmers to customise for themselves which the good parts and bad parts are and define their own subset, although naturally there are a number of pre-defined options. This is a really fantastic article on using JSHint; it's simple and aimed at having you using JSHint in a few minutes as well as providing various sources for pre-defined subsets.

Interesting article on prototypes: https://sporto.github.io/blog/2013/02/22/a-plain-english-guide-to-javascript-prototypes/