Skip to content

Effective JavaScript

July 13, 2013

This book by David Herman is definitely a must read for any serious JavaScript developer. It’s packed with 68 items that are great tips for writing correct JS programs.

Item 1: “use strict” whenever you can, but be warned about script concatenation between strict and non-strict JS code. Base to avoid combining these. Chrome 28 supports strict JS, as does Node.js

Item 2: All JS numbers are double-precision floating points. Integers can be represented perfectly from -2^53 to 2^53. However bitwise operators are special, because they convert arguments to 32-bit signed integers.

Item 3: Beware of implicit coercion. Objects are made into numbers by valueOf, and into Strings by toString. If your object implements one, it should implement both valueOf/toString.

Item 4: Beware of object wrapper, prefer primitives. Ex.

var s = new String("a");
undefined
> typeof s
'object'
> var t = "a";
undefined
> typeof t
'string'

Item 5: Avoid using ‘==’, use strict equality instead ‘===’ because of coercion issues.

Item 6: Learn automatic semicolon insertion limits. Use semicolons all the time won’t save you either, because of restricted productions.

return
{};
// parses into
return;
{};

Item 7: Strings are a sequence of 16-bit code units, not unicode code points. Unicode code points above 2^16 are represented by surrogate pairs. NOTE: Surrogate pairs throw off string length.

Item 8: Minimize use of global scope. Only scope exists inside functions. Use the global object only for feature detection.

Item 9: Always declare local variables with var. Otherwise they’ll be global.

Item 10: Avoid with.

Item 11: Understand closures in JS

Item 12: Variable hoisting. JS has lexical scoping, not block scoping. Manually declare your variables at the top of your functions to avoid confusion.

Item 13: Use IIFE (immediately invoked function expressions) to create local scope. This is needed because JS Closures capture outer variables by reference and not by value.

function wrapElements(a) {
var result = [];
for (var i=0, n=a.length; i < n; i++) {
result[i] = function() { return a[i]; };
}
return result;
}
var wrapped = wrapElements([10,20,30]);
var f = wrapped[0];
f(); // undefined

Item 14: Know the difference between function declaration and named function expression. Named function expressions improve debugging.

Item 15: Avoid block local function declaration to remain portable.

Item 16: Avoid creating local vars with eval.

Item 17: If eval is required, prefer indirect eval over direct eval.

var f = eval;
f("var x = 2;") // indirect eval

Item 18: Method calls provide receiver object to method as this. Functions have global as this (rarely useful). Constructors are called with ‘new’ and receive a fresh object as their receiver.

Item 19: Higher order functions take functions are arguments or return them. e.g. callbacks.

Item 20: Use call to provide a function a different receiver than it would normally have. f.call(obj, arg1, arg2)

Item 21: Use apply to call functions with unknown number of arguments. f.apply(object, args_array)

Item 22: In functions, use ‘arguments’ to create variable-arity functions.

Item 23: Never modify the ‘arguments’. If you need similar behavior use [].slice.call(arguments)

Item 24: If you have nested functions, bind arguments of each function to a variable so they can be accessed from different scopes.

Item 25: Use bind to extract methods with a fixed receiver. ‘bind’ creates a new function.

Item 26: Use ‘bind’ to curry functions.
var v = blah.map(someFunc.bind(null, arg1, arg2));

Item 27: Prefer APIs that accept functions to call rather than strings to eval.

Item 28: Do NOT rely on toString() from a Function to accurately represent the Function.

Item 29: Avoid nonstandard “arguments.caller” and “arguments.callee” because ‘caller’ is not even allowed in strict mode.

Item 30: C.prototype(), Object.getPrototypeOf(someObject1) == SomeObject.prototype // ES5 approach; or non-standard someObject.__proto__

Item 31: Prefer standard Object.getPrototypeOf(foo) to foo.__proto__

Item 32: Never modify __proto__ property, instead use Object.create to provide a custom prototype.

Item 33: Make constructors agnostic to ‘new’. Less brittle and protected against polluting the global scope.

function Foo(bar) {
if (!(this instanceof Foo)) {
return new Foo(bar);
}
this.bar = bar;
}

Item 34: Store methods on prototypes. Avoids duplication of methods on instances.

Item 35: Use closures to store private data

Item 36: Store instance state only in instance objects. Stateful data shouldn’t be shared via prototype.

Item 37: Understand this binding is always determined by the nearest enclosing function. Use name variable ‘self’, ‘me’, or ‘that’ to make this binding to inner functions.

Item 38: Call the superclass constructor explicitly from the subclass constructor. Use Object.create to construct a subclass prototype to avoid calling the superclass constructor.

Item 39: Be aware of all property names used by your superclass. Never reuse a superclass property name in a subclass.

Item 40: Avoid inheriting from standard classes, because they get special consideration. Prefer delegating to properties instead of inheriting.

Item 41: Objects are interfaces; prototypes are implementations. Avoid inspecting properties and prototypes of objects you don’t control.

Item 42: Avoid reckless monkey patching. Document any monkey patching to a library. Use monkey patching to provide polyfills for missing standard APIs.

Item 43: Use object literals to construct lightweight dictionaries.

Item 44: In ES5, use Object.create(null) to create prototype-free empty objects.

Item 45: Use hasOwnProperty to protect against prototype pollution.

Item 46: Prefer arrays to dictionaries for ordered collections.

Item 47: Never add enumerable properties to Object.prototype. Avoid adding properties to prototypes. If you must add properties to a prototype in ES5 do: Object.defineProperty as non-enumerable properties.

Item 48: Make sure not to modify an object while enumerating its properties with a for..in loop. Use a ‘while’ loop when properties might change.

Item 49: Prefer ‘for’ loops to ‘for..in’ loops for array iteration.

Item 50: Prefer iteration methods like map or forEach on Array.prototype

Item 51: Reuse generic Array methods on array-like objects by extracting method using ‘call’. Any object can use generic Array if it has indexed properties and an appropriate ‘length’ property.

Item 52: Prefer array literal to array constructor. ‘new Array(5);// empty array of size=5’ vs ‘new Array(1,2); // size=2 element1=1 element2=2

Item 53: When designing APIs maintain consistent conventions for variable names and function signatures.

Item 54: Use undefined as no value.

Item 55: Accept options objects for keyword arguments. The arguments in an options object should all be treated as optional.

Item 56: Prefer stateless APIs where possible. If stateful APIs are required, document their dependencies.

Item 57: Use structural/duck typing for flexible object interfaces. Allows for easy mocking for testing.

Item 58: Never overload structural types with other overlapping types. Distinguishing between true Arrays and Array-like objects.

Item 59: Avoid mixing coercions with overloading. Guard defensively against unexpected inputs.

Item 60: Use method chaining to combine stateless operations. Return new objects with each stateless chained method. Or for stateful chaining, return this.

Item 61: Don’t block the event queue waiting for I/O. Async APIs use callbacks to defer processing and avoid blocking the main app. JS accepts event concurrently, but processes event handlers sequentially using and event queue. Never use blocking I/O in the event queue.

Item 62: Use nested or named callbacks to perform several asynchronous operations in sequence. But still avoid sequential operations that can be performed concurrently.

Item 63: When using async APIs make sure you handle errors instead of just letting them drop.

Item 64: Use recursion to loop over async operations. Regular loops can not be asynchronous. A recursive function can be used to loop over async operations. Recursion performed over separate turns of the event queue does NOT overflow the call stack.

Item 65: Don’t block the event queue on computation. On platforms that support it, use the Worker API to start a new thread with it’s own event queue.

Item 66: Events in JS occur nondeterministically; unpredictable order. Use a counter to avoid data race conditions in concurrent operations.

Item 67: Never call asynchronous callbacks synchronously.

Item 68: Use Promises for cleaner async code. Promises represent eventual values. Use promises to compose different concurrent operations. Promises can also help avoid race conditions. Use select (also known as choose) where intentional race conditions are required.

Advertisements

From → Books, JavaScript

Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: