Giving you strong core JS skills, ready to tackle modern JS codebases!
// most code examples can be directly pasted
// in browser console
console.log((0, "what does this do?"));
Array.prototype.includes
+ N**k
// <- first non-comment line of file or function
"use strict";
// or: ES6 modules/classes
with
All code will run in strict, with ES6.
./exercises/some-name
exercise.js
verify.js
cd ./exercises/some-name
node verify
Make smaller things.
Sandii Metz
// greetings.en.js
export var informal = "hi";
// run.js
import { informal } from "./greetings.en";
console.log(informal + " amy") // hi amy
// greetings.es.js
export default "hola";
// greetings.es.js
var greeting = "hola";
export default greeting;
// greetings.es.js
var greeting = "hola";
export { greeting as default };
// run.js
import defaultSpanish from "./greetings.es";
console.log(defaultSpanish + " amy"); // prints 'hola amy'
var cake = "sponge";
var tea = "darjeeling";
export { tea, cake as treat };
import { tea as drink, treat } from "./teaParty";
import { default as host } from "./teaParty";
// default + values
import party, { tea, treat } from "./teaParty";
import * as party from "./teaParty";
var cake = party.cake;
export { party as default, cake as treat };
exercises/modules;
Node's module system
// export { mainMethod as default }
module.exports = exports = mainMethod;
// export function someFunction() {}
exports.someFunction = function() {}
function mainMethod() {
}
// relative or absolute path
var myModule = require("./my/module");
// what's this?
var aModule = require("./some/other/module");
// third party?
var fs = require("fs");
var readFile = fs.readFile;
// third party?
var someModule = require("lodash");
// this is from core library
var fs = require("fs");
// this is looked up from node_modules folders in current,
// and parent directories
var anNpmModule = require("from-npm");
⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️
⭐️ ⭐️
⭐️ 'Behind the scenes' ⭐️
⭐️ var module = { exports: {} }; ⭐️
⭐️ var exports = module.exports; ⭐️
⭐️ ⭐️
⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️
exports.helper = function() {
}
module.exports = function Main() {
}
module.exports
!
exports
object is no longer in module object.
exercises/commonjs
var
let
, const
var
var someVariable;
var anotherVariable = "hello";
undefined
vs undeclaredundefined
var notDefined;
console.log(notDefined); // ?
var someVar;
console.log(smeVar); // typo, what happens??
var
= function scopedfunction main(A) {
console.log(A);
}
main(10);
main("hello " + 5);
function main(A) {
console.log(A);
}
var A = 10;
main(A);
main(A + 5);
function main() {
var A = 'hello';
var B = 'hi';
function one(A, C) {
var D = 'hola';
console.log(A,B,C,D);
}
one(A, 'yo');
one(A);
one(B,A);
console.log(D); // <- what happens here?
}
function main() {
// what happens here?
console.log(i);
for(var i = 0; i<10; i++) {
console.log(i);
}
}
function main() {
console.log(i);
for(var i = 0; i<10; i++) {
console.log(i);
}
}
// visualising the 'hoisting zone'
function main() {
var i;
⚡️⚡️⚡️⚡️⚡️⚡️⚡️⚡️⚡️⚡️⚡️⚡️⚡️
console.log(i);
for(var i = 0; i<10; i++) {
console.log(i);
}
}
// browser - strict mode
window.something = "I am global";
// is now referencable anywhere in program
console.log(something);
// node
global.something = "I am global";
this; // sloppy, outside functions
window; // browser
global; // node
const
const doesNotChange = "hi";
// throws TypeError - Assignment to constant
doesNotChange = "I'll try my luck";
const
const counter = { count: 0 };
// what happens here?
counter.count += 1;
const
if possiblelet
let hiThere = "hello";
if(true) {
let hiThere = "GOODBYE";
}
console.log(hiThere) // which?
for(let i = 0; i++; i < 10) {
console.log(i);
}
// reference error
console.log(i);
const
, then let
over var
exercises/variables-and-scopes
const [breakfast, lunch, ...more]
= ["eggs", "falafel", "high tea", "a feast!", "hot chocolate"];
console.log(breakfast) // "eggs"
console.log(more) // ["high tea", "a feast!", "hot chocolate"];
PATTERN DEFINING VARIABLES = VALUE TO DESTRUCTURE;
const guest = { name: "Hare", treat: "chocolate eggs" };
const { name, treat } = guest;
console.log(name) // Hare
const guest = { name: "Hare", treat: "chocolate eggs" };
const { name: guestName, treat } = guest;
console.log(guestName) // Hare
const guest = { name: "Hare", treat: "chocolate eggs" };
let name, treat;
// syntax issue: can't have a naked {} on left hand
({ name, treat } = guest);
console.log(name, mood) // Hare, Jumpy
const notGuest = 1;
const { name, treat } = notGuest;
console.log(name, treat) // undefined, undefined
const guest = { name: "Hare", treat: "chocolate eggs" };
const { name, treat, mood = "jumpy" } = guest;
console.log(name, mood) // Hare, Jumpy
const [ firstGuest = "cheshire cat" ] = [];
const guest = {
name: "Hare",
treats: ["chocolate eggs", "toast", "rich tea biscuits"],
};
const { name, treats: [best, ...rest], mood = "jumpy" } = guest;
console.log(name, rest) // Hare, ["toast", "rich tea biscuits"]
exercises/destructuring
var name = 'Tim';
var description = "It's saving escapes";
var complete = `${name}: ${opinion}`;
${}
var opinionOnEs6 = "much nicer";
var es6 = `Interpolation: '${opinion}'`;
var opinionOnEs5 = "looks ugly";
var es5 = "Interpolation: '" + opinion + "'";
var es5Html = "<h1>Ugh</h1>" +
"<p>This is horrid</p>";
var howMuch = "Much";
var html = `
<h1>Writing HTML with ES.Next</h1>
<p>${howMuch} nicer</p>
`;
null
& undefined
undefined
==
has issuesvar A = "\t\t\t\t\t \n \n \n" == false;
if(A) {
console.log("A was == false");
} else {
console.log("A wasn't == false");
}
===
==
null
and undefined
var undefinedNotEqeqeqNull = undefined === null; // false
if(x === null || x === undefined) {
// what a chore!
}
if(x == null) {
// equivalent, shorter, nicer
}
== null
var nullish = x == null;
===
Same value?
Special cases: NaN !== NaN
==
null
or undefined
var launch = new Boolean(false);
if(launch)
console.log("launching missiles");
else
console.log("phew");
console.log("peace at last");
// what is output?
npm run lint:all
# per exercise
eslint exercises/some-exercise/exercise.js
npm install --global eslint@latest
eslint path/to/js
// brevity at all cost!
yes && doThing()
if(yes) {
doThing();
}
eslint --fix exercises/some-exercise/exercise.js
lint:fix
that runs lint with fixnpm run
on its own lists scriptsfunction x([a,b]) {
return (...args) => args.map(x => x + a + b);
}
vs
"use strict";
var _slicedToArray = function () { /* 0.7kb of JS... */ }
function x(_ref) {
var _ref2 = _slicedToArray(_ref, 2);
var a = _ref2[0];
var b = _ref2[1];
return function () {
for (var _len = arguments.length, args = Array(_len),
_key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return args.map(function (x) {
return x + a + b;
});
};
}
> node -v
5.5.0
> npm install --save babel-preset-node5
function add(a,b) {
return a + b;
}
var add = function(a, b) {
return a + b;
}
// note: lack of `return`
var add = (a, b) => a + b;
// no ( ) for single arg
var id = object => object.id;
// note: multiple statements requires {} and `return`
var twoStep = (a, b) => { console.log(a); return b }
// returning just an object requires you provide brackets:
var object = (a, b) => {{a:1, b:2}}
- Is a fat-arrow an expression?
// default parameters
function log(n, base = 10, opts = {}) {
// ...
}
// default parameters
function log(n = required("number"), base = 10) {
// ...
}
function required(name) {
throw new Error(name + " is a required argument");
}
function log({ n, base }) {
return Math.log(n) / Math.log(base);
}
log({ n: 64, base: 4 }) // 3
log({ n: 90, base: 10 }) // 1.954...
// team up with required!
function log({ n = required("n"), base = required("base") }) {
return Math.log(n) / Math.log(base);
}
function required(name) {
throw new Error(name + " is a required argument");
}
function log({ n, base = 2 }) {
return Math.log(n) / Math.log(base);
}
console.log(log({ n: 8 })) // 3
log() // TypeError: Cannot match against 'undefined' or 'null'
// How to fix?
function log({ n, base = 2 } = {}) {
return Math.log(n) / Math.log(base);
}
console.log(log({ n: 8 })) // 3
log() // NaN
// variable length functions
function sprintf(format, ...args) {
console.log(format, ...args);
}
const pair = [1,2];
// can use ... in calls too!
sprint("%s %s", ...pair);
exercises/functions-core
We can see variables in functions that wrap us (red)
We can't see variables from inner functions (blue)
var elements = document.querySelectorAll("button");
// What is wrong here? How can we solve?
for(var i = 0; i < elements.length; i++) {
var element = elements[i];
element.addEventListener("click", function() {
console.log(element.innerHTML);
});
}
exercise/closures
Array
const primeMinisters = ["Cameron", "Brown", "Blair", "Major"];
primeMinisters[0] // What would this be?
// ...and this?
primeMinisters[primeMinisters.length - 1]
// ...and this?
primeMinisters[-1]
console.log(primeMinisters.length) // 4
const primeMinisters = ["Cameron", "Brown", "Blair", "Major"];
primeMinisters.unshift("Fry"); // at start
primeMinisters.push("Thatcher"); // at end
// what did we learn about const?
console.log(primeMinisters.length) // 6
primeMinisters.pop(); // drops end
primeMinisters.shift(); // drops start
console.log(primeMinisters.length) // 4
const primeMinisters = ["Cameron", "Brown", "Blair", "Major"];
const lastLabourGovernment = primeMinisters.slice(1, 3);
// Blair then Brown
lastLabourGovernment.reverse().join(" then ")
// how does slice() work?
const primeMinisters = ["Cameron", "Brown", "Blair", "Major"];
primeMinisters.sort();
// [ 'Blair', 'Brown', 'Cameron', 'Major' ]
console.log(primeMinisters)
// what can we tell about .sort() and mutation?
// and sort order?
primeMinisters.sort((a,b) => a.length - b.length)
// [ 'Blair', 'Brown', 'Major', 'Cameron' ]
console.log(primeMinisters)
for ... in
const primeMinisters = ["Cameron", "Brown", "Blair", "Major"];
// what... is this allowed?
primeMinisters.metaData = "hi";
// for ... in will go over all properties of arrays
var initials = [];
for(let i in primeMinisters) {
initials.push( primeMinisters[i][0] );
}
// what does initials end up as?
Array
can have string properties!for .. in
iterates string propertiesfor
const primeMinisters = ["Cameron", "Brown", "Blair", "Major"];
const initials = [];
for(let i = 0, l = primeMinisters.length; i < l; i++) {
initials[i] = primeMinisters[i][0];
}
for .. of
const primeMinisters = ["Cameron", "Brown", "Blair", "Major"];
primeMinisters.metaData = "hi";
// ES6 `for ... of` goes over VALUES of iterables
const initials = [];
for(let pm of primeMinisters) {
initials.push(pm[0]);
}
console.log(initials) // ["C","B","B","M"]
for ... in
on arrays// ES5 'array additions'
const primeMinisters = ["Cameron", "Brown", "Blair", "Major"];
primeMinisters.filter((p) => p[0] === "B") // ["Brown","Blair"];
primeMinisters.map((p) => p[0]) // [ "C", "B", "B", "M" ]
primeMinisters.reduce((s, e) => s + ":" + e) // 'Cameron:Brown:Blair:Major'
const byValue = [1,2] === [1,2]; // false
const couldStringify = JSON.stringify([1,2])
=== JSON.stringify([1,2]); // true
exercises/arrays
Object
used as dictionaryconst enToFr = {};
enToFr.hello = "bonjour";
enToFr.cheese = "fromage";
// equivalent
console.log(enToFr.hello);
console.log(enToFr["hello"]);
AKA hashmap, associative array, table.
const dict = {};
dict[10] = "ten";
console.log(dict["10"]); // "ten"
// general rule: calls toString on non-strings
const convertsToTen = {
toString: () => "10"
};
console.log(dict[convertsToTen]); // "ten"
for ... in
for(const property in Object.values(object)) {
const value = object[property];
console.log(`${property}: ${value}`);
}
for ... of
// objects not iterable by default, so...
for(const [property, value] of entries(object)) {
console.log(`${property}: ${value}`);
}
// Object.entries(obj) is proposed for future ECMA
// ES6
function create(property, value) {
return { ["id:" + property]: value };
}
// ES5
function create(property, value) {
var o = {};
o["id:" + property] = value;
return o;
}
const dict = {};
dict[1] = "hello";
// how to count?
console.log(dict.length); // undefined
console.log(Object.keys(dict).length); // slow for very large objects
const m = new Map;
const o = {};
const a = [1,2];
const b = [1,2];
// assign
m.set(a, "A");
m.set(b, "B");
o = {[a]: "A", [b]: "B"}
console.log(Object.keys(o).length); // 1
console.log(m.size); // 2
// ...why?
Map
const m = new Map;
const list = [1,2,3];
m.set(list, "A");
console.log(m.get(list)); // A
console.log(m.get([1,2,3])); // ...?
const m2 = new Map([
["list", "hello"],
["boo", "another key"],
]);
for .. of
const m = new Map;
m.set("A", 1);
m.set("B", 2);
for(const prop in m) {
console.log(prop) // not called! why?
}
for(const kv of m) {
console.log(kv) // ["A", 1], ...
}
for(const k of m.keys()) {
console.log(k) // "A", "B"
}
for(const v of m.values()) {
console.log(v) // 1, 2
}
m.forEach((v, k) => console.log(v, k))
Set
// ES6 only
const s = new Set;
s.add(1);
console.log(s.has(1)); // ...?
console.log(s.size); // ...?
s.add(1); s.add(1); s.add(1);
console.log(s.size); // ...?
const A = [1,2];
s.add(A);
console.log(s.has(A)); // ...?
console.log(s.has([1,2])); // ...?
exercises/maps-objects-sets
1 // we'll not continue to the next line until the OS
2 // has read the file and provided it back to our process
3 var contents = fs.readFileSync("/tmp/hello", "utf8");
4
5 console.log("End of source file");
1 fs.readFile("/tmp/hello", { encoding: "utf8" },
2 function(err, content) {
3 // (2/2) ...we get here
4 console.log("Read the file");
5 });
6
7 // (1/2) we'll get here waaaaaaaaaaaay before...
8 console.log("End of source file");
setTimeout
fs
, child_process
)try ... catch
try ... catch
in the stack, we're okfunction main() {
try {
helper();
} catch(e) {
console.log(e);
}
}
function helper() {
throw Error("bad thing");
}
try ... catch
can't help youfunction main() {
try {
// make a async HTTP request
$.get("/some/json/url");
} catch(e) {
console.log(e);
}
}
exercises/callbacks-and-async
.then(transformResolve, transformReject)
const user = getUser(params["id"]);
const profile = user.then(getProfile);
const accountPage = Promise.all([user,profile])
.then(accountPageTemplate);
accountPage.then(
renderHtml,
renderErrorMessage
);
then
isPromise.resolve(x)
Promise.reject(Error("some description"))
Error
for stacksPromise.all([promise, value, promiseB, valueB])
Array
, Map
etc)SomeQuery
.then(transform)
.then(function(value) {
if(!isOk(value)) {
return Promise.reject(Error("Not ok!"))
}
return someTest(value)) ?
QueryA(value) : QueryB(value);
})
.catch((err) => Promise.reject(Error("Query failed: " + err)))
.finally(function() {
// anything that happens if resolved/rejected
});
var userIds = [10,20,30,50,100];
// array of promises
var users = userIds.map(User.get);
// promise for array of [user, account]
var usersWithAccounts = Promise.all(users)
.then(function(users) {
return users.map(function(user) {
return Promise.all([
user,
Account.get(user)
]);
});
});
class
class Widget {
constructor({ model }) {
this._model = model;
}
render() {
return `<div>${ this._model.get("name") }</div>`;
}
}
const instance = new Widget({
model: new Map([["name", "crockford"]]),
});
class Widget {
// this is the function invoked when we call 'new Widget'
constructor({ model }) {
// ...
}
}
class Widget {
// ...
// methods defined here will be callable on the instances
render() {
}
}
Exercise 1
exericses/objects-and-prototypes
class Widget {
render() {
return `<div>${ this._renderName() }</div>`;
}
// prefix to show it's private
_renderHelper() {
return `<span class='name'>
${ this._model.get("name") }
</span>`;
}
}
const instance = new Widget(/* ... */);
instance.render();
instance._renderHelper(); // no error
const privates = new WeakMap;
class Counter {
constructor() {
privates.set(this, { count: 0 })
}
get() {
return privates.get(this).count;
}
add() {
privates.get(this).count += 1;
}
}
const instance = new Counter(/* ... */);
instance.add();
instance.add();
console.log(instance.get()); // 2
WeakMap
?WeakMap
e.gconst map = new Map;
const weak = new WeakMap;
const A = {};
const B = {};
const C = {};
map.set(A, "");
weak.set(B, "");
weak.set(B, "");
weak.set(C, "");
global.map = map;
global.weak = weak;
class DefaultMap extends Map {
constructor(members, defaulter) {
super(members);
this._default = defaulter;
}
get(key) {
if(!this.has(key)) {
this._default(key, this);
}
return super.get(key);
}
}
const instance = new DefaultMap([["apples", 7 ]]),
() => 0)
console.log(instance.get("apples")) // 7
console.log(instance.get("pears")) // 0
class DefaultMap extends Map {
constructor(members, default) {
super(members);
this._default = default;
}
// ...
}
// this is the default constructor of a sub-class
constructor(...args) {
super(...args)
}
class DefaultMap extends Map {
// ...
get(key) {
// ...
return super.get(key);
}
}
Exercise 2
exercises/objects-and-prototypes
class Widget {
something: [];
}
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
// always at run-time
function getProperty(object, prop) {
if(object.hasOwnProperty(prop)) {
return object[prop];
} else {
if(object.__proto__) {
getProperty(object.__proto__, prop);
} else {
return undefined;
}
}
}
class Widget {
}
// I'm bored of initialising in constructor!
Widget.prototype.letters = [];
const A = new Widget;
const B = new Widget;
A.letters.push("a");
console.log(B.letters) // ["a"]
const sturcture = {
squareArea: function() {
return this.width * this.height;
},
};
const house = Object.create(structure);
Object.assign(house, {
width: 5,
height: 10,
});
const shed = Object.create(structure);
Object.assign(shed, {
width: 2,
height: 2,
});
house.squareArea() // 50
shed.squareArea() // 4
this
on invocationconst sturcture = {
squareArea: function() {
return this.width * this.height;
},
};
const house = Object.create(structure);
Object.assign(house, {
width: 5,
height: 10,
});
house.squareArea() // 50
this
// this = someObject
someObject.someProperty();
const someProperty = this.someObject;
// this = enclosing this, likely global
someProperty();
var someObject = {
name: "lil' listeny",
listen: function(el) {
el.addEventListener("click", this.handle)
},
handle: function() {
console.log(this.name);
},
};
// what do we see console.log'd?
someObject.listen(button);
button.click();
=>
var someObject = {
// ...
listen: function(el) {
// fat arrow has lexical this! i.e this of current scope
el.addEventListener("click", (e) => this.handle(e))
},
// ...
};
fn.bind(thisValue)
var someObject = {
// ...
listen: function(el) {
// .bind returns a new fn, as if bound to this via closure
el.addEventListener("click", this.handle.bind(this))
},
// ...
};
bind
function bind(fn, thisValue) {
return function() {
return fn.apply(thisValue, arguments);
}
}
new
function Structure(width, height) {
this.width = width;
this.height = height;
}
Structure.prototype = {
squareArea: function() {
return this.width * this.height;
},
};
function House(w, h, bedrooms) {
Structure.call(this, w, h);
this.bedrooms = bedrooms;
}
House.prototype = new Structure;
House.prototype.sleeps = function() {
return this.bedrooms.reduce((s,b) => s + b.sleeps, 0)
};
.call(thisValue, ...args)
function House(w, h, bedrooms) {
Structure.call(this, w, h);
this.bedrooms = bedrooms;
}
function Structure(width, height) {
// our 'this' value when we call Structure.call above
// will be an object { proto: House.prototype }
this.width = width;
}
Exercise 3
exercises/objects-and-prototypes
describe("user login", function() {
})
describe("user login", function() {
it("starts off with username field focussed");
it("warns if the password is too simple");
it("prevents sumission if invalid");
})
describe("user login", function() {
it("starts off with username field focussed", function() {
const widget = new Widget();
widget.render();
const input = widget.el.find("[name=username");
assert.elementHasFocus(input);
});
})
class Widget {
render() {
// implement
}
}
// our higher level test may prompt this:
// it("starts off with username field focussed");
describe("auto-focus component", function() {
it("focusses an input");
it("does not prevent focus leaving the input");
})
describe("auto-focus component", function() {
let widget;
beforeEach(function() {
// put our shared setup in here, save typing
widget = new Widget();
});
})
Exercise 1
exercises/testing
// callback based - mocha supports promises too
it("should call", function(done) {
somethingAsync(function(url) {
assert.match(url, /users\/1/)
done();
})
})
it("should call", function(done) {
// Faking out $.get
$.get = function(url) {
assert.match(url, /users\/1/)
done();
}
user.retrieve(1);
})
function computeAnswer(cb) {
$.get("/answer",function(answer) {
cb(answer + 1);
});
}
// STUB!
$.get = function(url, cb) {
cb(42);
}
computeAnswer(function(answer) {
assert.equals(result,43);
});
Exercise 2
exercises/testing
node-debugger
in Chrome, google and open 'ember TodoMVC'
// over arrays
for(const v of values) {
}
for(const [index, v] of values.entries()) {
}
// over maps
for(const [k,v] of map.entries()) {
}
function *objectProperties(object) {
const keys = Object.keys(object);
for(const key of keys) {
yield [key, object[key]];
}
}
for(const [k, v] of objectProperties({a: 1, b: 2, c: 3})) {
console.log(k, v);
}
*
// the '*' means we're a generator function
function *objectProperties(object) {
// ...
}
yield
function *abc() {
// yield returns a value to the caller (for ... of above)
yield "a";
yield "b";
yield "c";
}
logLast([1,2,3,4])
function *logLast(xs) {
console.log(xs[xs.length - 1]);
}
// what's output?
for(const k in reverse([1,2,3,4])) {
console.log(k);
}
function *reverse(xs) {
for(var i = xs.length; i--;) {
yield xs[i];
}
}
for ... of
, in
will NOT workexercises/generators
const gen = someGeneratorFunction();
console.log(typeof gen.next); // function
.next(passToYield)
yield
the valuefunction* echo() {
let value;
while(true) {
value = yield value;
}
}
const g = echo();
console.log(g.next("one")) // { done: false, value: undefined }
console.log(g.next("two")) // { done: false, value: "two" }
console.log(g.next("three")) // { done: false, value: "three" }
// why...?
console.log(dig({ a: { b: "here" }}, ab)) // "here"
console.log(dig({ z: { b: "here" }}, ab)) // undefined
function *ab() {
yield "a";
yield "b";
}
function dig(object, create) {
const g = create();
while(object) {
let step = g.next();
if(step.done) {
break;
} else {
object = object[step.value];
}
}
return object;
}
exercise/generators-helpers
function cat(pathA, pathB, cb) {
fs.readFile(pathA, function(err, fileA) {
if(err) return cb(err);
fs.readFile(pathB, function(err, fileB) {
if(err) return cb(err);
combine(fileA, fileB, cb);
});
})
}
function cat(pathA, pathB) {
return Promise.all([
fs.readFileAsync(pathA),
fs.readFileAsync(pathB),
]).then([fileA, fileB]) => {
return combine(fileA, fileB);
});
}
import co from "co";
co(function *cat() {
const fileA = yield fs.readFileAsync(pathA);
const fileB = yield fs.readFileAsync(pathB);
return combine(fileA, fileB);
})
.then(function(combined) {
console.log(combined);
});
yield
a promise, co will call .next
after the promise resolvesimport co from "co";
const cat = co.wrap(function *cat(pathA, pathB) {
const readingA = fs.readFileAsync(pathA);
yield fs.readFileAsync(pathB);
const fileA = yield readingA;
return combine(fileA, fileB);
});
// files contain upper case words
cat("./one", "./two")
.then(function(combined) {
// ONETWO
console.log(combined);
});
import co from "co";
co(function *cat() {
// don't yield yet...
const readingA = fs.readFileAsync(pathA);
// ...until we've started the other tasks
// to run in parallel
const fileB = yield fs.readFileAsync(pathB);
const fileA = yield readingA;
return combine(fileA, fileB);
})
exercise/generators-co
for .. of
Map
s etcfor(const [value, left, right] of myTree) {
// pretty cooooool....
}
Symbol
sconst s1 = Symbol("A");
const s2 = Symbol("B");
const s3 = Symbol("B");
console.log(s2 === s3) // ?
// like unix 'tac' method
Object.defineProperty(String.prototype, "tac", {
value: function(...others) {
return others.slice().reverse().join("") + this;
}
});
console.log("hello".tac("1","2","3")) // 321hello
const tacOne = Symbol("tac");
const tacTwo = Symbol("tac");
Object.defineProperty(String.prototype, tacOne, {
value: function(...others) {
return others.slice().reverse().join("") + this;
}
});
Object.defineProperty(String.prototype, tacTwo, {
value: () => { throw Error("BROOOOKEN") }
});
console.log("hello"tacOne) // 321hello
function L(value, next) {
return { value, next };
}
const list = L(1, L(2, L(3)));
const items = [];
for(let node = list; node; node = node.next) {
items.push(node.value);
}
console.log(items); // [1, 2, 3]
// this is a bit faffy, and fragile
const items = [];
for(let node = list; node; node = node.next) {
items.push(node.value);
}
const items = [];
// WOAH!!!
for(const value of list) {
items.push(node.value);
}
.next
, .value
)list
could be replaced with Array
, Set
{
*Symbol.iterator {
// yields each value in data-structure in turn
}
}
function L(value, next) {
return { value, next,
[Symbol.iterator]: iterateLinkedList };
}
function *iterateLinkedList() {
for(let node = list; node; node = node.next) {
yield node.value;
}
}
const list = L(1, L(2, L("banana")));
for(const v of list) console.log(v);
exercises/generators-iterators
For tips & content, sign up to my mailing list: