· articles
The Good and Bad Parts of JS (according to Douglas Crockford)
Notes of the good parts of Javascript and the parts to avoid according to Douglas Crockford's books and lectures.
Javascript, having famously been created in ten days, has some weird parts. Some of the baggage is from being around for so long and from being a creation of the ECMAScript TC39 committee. Despite all of that, it is still a great language with a lot of power and expressiveness. Douglas Crockford contends the detractors of Javascript mainly get frustrated with the language because they approach it from an Object Oriented angle instead of a Functional angle (it is based on Scheme after all, a 1970’s FP language). This is by no means exhaustive, but here are some notes on What Crockford considers good (and bad) about JS:
The Good Parts
First Class Functions or Higher Order Functions
Functions in JS can take functions as parameters and return functions as values. Javascript has always had this (that’s why I list it separately), as it was an adopted feature from Scheme. This permits powerful Currying, Closure patterns and the Continuation Passing Style.
Good Parts from ES6+
- Proper Tail Calls for Recursive Function optimization
// The bad way:
function repeat(func) {
while (func() !== undefined) {
repeat(func);
}
}
// Proper Tail Call way:
function repeat(func) {
if (func() !== undefined) {
return repeat(func);
}
}
The second example enjoys a performance boost by using less memory due to the Tail call. Proper Tail Calls (i.e. tail call optimization) is simply when the last line of your function returns the self-invoked function. The optimization is from the stack frame to remove old function stack traces and replace it with the newly invoked trace, so there isn’t a memory overflow threat.
- The Spread and Rest Operator
[...array]
- The Module System
let
andconst
- Destructuring allows for easy RORO (receive object, return object) design patterns
- Improved Object looping:
Object.keys.forEach()
- WeakMap(): Works how Objects should have worked from the start with auto-garbage collection.
- Template String Literals (great for templating and breaking up regex)
`Hello ${name}`;
- Arrow Functions (aka Lambdas): Great as callbacks/predicates
- The suitability towards the Functional programming style
// The JS way to do class-free OOP using Functions:
function constructor(spec) {
let { member } = spec,
{ other } = other_constructor(spec),
method = function () {
/* member, other, method, spec */
};
return Object.freeze({
method,
other,
});
}
The Bad Parts
The Old
- The
new
keyword (error-prone, prefer the literal syntax) - Object.create (prefer literal syntax or Object.assign({}, source) to avoid pass by reference errors)
- The
this
keyword (error prone due to unintuitive value adoption) - label breaks
- prototypes, prototypal inheritance (error prone due to confusion from Own vs. Inherited properties)
Note from the author: I disagree with Crockford on this point, I found prototypal inheritance to be much more intuitive than classical inheritance
null
(There should only beundefined
ornull
, not both. Sincenull
regrettably returns typeof asobject
, it ought to be the one removed)- falsiness (Coercion in general due to unexpected values, but falsiness in particular trips up control flow quite often with values like
''
and0
) for
loops andfor...in
loops (prefer the `.forEach() syntax)while
loops (prefer recursive functions with tail calls)
Bad Parts from ES6+
Classes
This is only syntactic sugar over functions anyways and it does not change the prototypal inheritance model to the classical inheritance model, so it threatens to mislead OOP programmers about how properties are truly being inherited.
Generators
Crockford does not like these because he claims they are inherently “stateful” and cannot be pure functions (i.e. functions without side effects). Crockford feels that unaccounted for “statefulness” accounts for a large number of bugs/errors and so he prefers using pure functions whenever possible, and so dismisses generators since they are inherently impure.
Note from the author: I somewhat disagree with Crockford’s thoughts on this one: While generators CAN be stateful, they certainly do not have to be. It depends on how you author it. I do agree that generators cannot really be pure functions, but that is ok since they can serve as wrappers to other pure functions, which provide a nice Iterable interface for those pure functions. And having the control of an Iterator by default is a powerful interface for dealing with returned values.
Overall I feel that I am in agreement with Crockford’s opinions about which parts of Javascript to lean on and which parts to avoid. In particular, Javascript’s origins from the Scheme language mean it is best used in the functional styleso it can take advantage of higher order functions, currying, closures, and recursion. And in general I agree about avoiding OOP patterns. Although, there is no need for fanatical avoidance of OOP, both FP and OOP can be used together to great effect.
I mentioned the two places already where I disgree with Crockford on his thoughts here: Prototypes and Generators. I think both of these language features actually have tremendous strengths. This is largely due to lessons I’ve absorbed from another great JS teacher, Kyle Simpson. I will have a similar notes-dump post about his thoughts in the future.