for (i = 0, n = player.levels.length; i < n; i++) { sum += player.levels[i].score; } return sum; } function averageScore(players) { sum = 0; for (i = 0, n = players.length; i < n; i++) { sum += score(players[i]); } return sum / n; }
// globals function score(player) { sum = 0; for (i = 0, n = player.levels.length; i < n; i++) { sum += player.levels[i].score; } return sum; } var i, n, sum; // same globals as score! function averageScore(players) { sum = 0; for (i = 0, n = players.length; i < n; i++) { sum += score(players[i]); } return sum / n; }
i, n, sum; sum = 0; for (i = 0, n = player.levels.length; i < n; i++) { sum += player.levels[i].score; } return sum; } function averageScore(players) { var i, n, sum; sum = 0; for (i = 0, n = players.length; i < n; i++) { sum += score(players[i]); } return sum / n; }
= 30; // "x" is from `Object.prototype` because // the global object inherits from it console.log(x); // 10 (function foo() { var w = 40, x = 100; // "x" is from `Object.prototype` // because {z: 50} inherits from it with ({z: 50}) { console.log(w, x, y, z); // 40, 10, 30, 50 } console.log(x, w); // 100, 40 console.log(global.w); // 20 })();
function testDirect() { var x = "local"; return eval("x"); // direct eval } testDirect(); // "local" function testIndirect() { var x = "local"; var f = eval; return f("x"); // indirect eval } testIndirect(); // "global" (0,eval)("string"); // terse indirect eval
goal: call `f` with `o` as the context o.temporary = f; // what if o.temporary already existed? var result = o.temporary(arg1, arg2, arg3); delete o.temporary; // what if o.temporary already existed? // instead, use `call` f.call(o, arg1, arg2, arg3);
Arguments // this becomes a little more difficult with constructors function NumberContainer(){ this.nums = [].map.call(arguments, function(n){ return +n; }); } NumberContainer.prototype.mean = function(){ return [].reduce.call(arguments, function(memo, n){ return memo + n; }, 0) / arguments.length; }; new NumberContainer(1, 2, 3).mean(); // 2 // goal: compute the mean of the values in this list var array = [2, 7, 1, 8, 2, 8, 1, 8]; var o = Object.create(NumberContainer.prototype); NumberContainer.apply(o, array); o.mean(); // 4.625
don't mention `this` function hello(username) { return "hello, " + username; } hello("Keyser Söze"); // "hello, Keyser Söze" // "methods" mention `this` var o = { hello: function() { return "hello, " + this.username; }, username: "Hans Gruber" }; o.hello(); // "hello, Hans Gruber" // constructors are meant to be used with `new` and // usually have a meaningful prototype property function Cow(){} Cow.prototype = Object.create(Animal.prototype); new Cow;
is used to establish the [[Prototype]] of objects created by new C. ▪ Object.getPrototypeOf(o) is the standard ES5 mechanism for retrieving o’s [[Prototype]] object. ▪ o.__proto__ is a non-standard mechanism for retrieving or re-assigning o’s [[Prototype]] object.
x, y) { this.scene = scene; this.x = x; this.y = y; scene.register(this); } function SpaceShip(scene, x, y) { Actor.call(this, scene, x, y); this.points = 0; } // In order for SpaceShip to be a proper subclass of Actor, // its prototype must inherit from Actor.prototype. SpaceShip.prototype = new Actor(/* ?? */); SpaceShip.prototype = Object.create(Actor.prototype);
= path; for (var i = 0, n = entries.length; i < n; i++) { this[i] = entries[i]; } } // attempt to subclass Array Dir.prototype = Object.create(Array.prototype); var dir = new Dir("/tmp/mysite", ["index.html", "script.js"]); // Unfortunately, this approach breaks the `length` and // internal `[[Class]]` properties of arrays: dir.length; // 0 {}.toString.call(dir); // "[object Object]" {}.toString.call([]); // "[object Array]"
[98, 74, 85, 77, 93, 100, 89], mean, total; total = 0; for (var score in scores) { total += score; } mean = total / scores.length; mean; // 17636.571428571428 total; // "00123456" total = 0; for (var i = 0 , n = scores.length; i < n; ++i) { total += scores[i]; } mean = total / scores.length; // 88
{ var result = []; for (var key in this) { result.push(key); } return result; }; // Sadly, this method pollutes even its own result: ({ a: 1, b: 2, c: 3 }).allKeys(); // ["allKeys", "a", "b", "c"]
(var i = 0; i <= n; ++i) { ... } // extra end iteration for (var i = 1; i < n; ++i) { ... } // missing first iteration for (var i = n; i >= 0; --i) { ... } // extra start iteration for (var i = n - 1; i > 0; --i) { ... } // missing last iteration // replace loops with iteration methods for (var i = 0, n = players.length; i < n; i++) { ++players[i].score; } players.forEach(function(player) { ++player.score; }); // iteration methods also abstract common patterns var trimmed = []; for (var i = 0, n = input.length; i < n; i++) { trimmed.push(input[i].trim()); } var trimmed = input.map(function(s) { return s.trim(); });
Array.prototype.concat; it is not generic function namesColumn() { return ["Names"].concat(arguments); } namesColumn("Alice", "Bob", "Chris"); // ["Names", { 0: "Alice", 1: "Bob", 2: "Chris" }] // shallow copy `arguments` so we have a real array function namesColumn() { return ["Names"].concat([].slice.call(arguments)); } // ["Names", "Alice", "Bob", "Chris"]
= downloadSync("http://example.com/file.txt"); // blocked console.log(text); downloadAsync("http://example.com/file.txt", function(text) { // continues here when I/O has completed console.log(text); }); // queues I/O and arrives here immediately console.log("guaranteed to execute before the above continuation");
Harness the Power of JavaScript; Addison-Wesley Professional; 1st Edition, 2012. • Soshnikov, Dmitry. JavaScript. The Core.; <http://dmitrysoshnikov. com/ecmascript/javascript-the-core/>