let
versus var
var
is quicksand
var cat = 'louis'; var i; for (i = 1; (i <= 10); i++) { var cat = 'loopy cat #' + i; console.log(cat); } console.log(cat); // cat #10, not louis!
let
versus var
var
is quicksandvar cat = 'louis'; var i; for (i = 1; (i <= 10); i++) { var cat = 'loopy cat #' + i; console.log(cat); } console.log(cat); // cat #10, not louis!
let
versus var
let
: so scope-y freshlet cat = 'louis'; for (let i = 1; (i <= 10); i++) { let cat = 'loopy cat #' + i; console.log(cat); } // louis the cat came back! console.log(cat);
const
makes you thinkclass Cat { constructor(name) { this.name = name; } pet() { console.log(this.name + ' purrs.'); } }
let cat; cat = new Cat('Louis'); cat.pet(); cat = new Cat('SpySpy'); // I have to read the previous line // to know which cat this refers to cat.pet(); // More than one Louis? Surely you jest! cat = new Cat('Louis');
const
makes you thinkconst
: clear & safeclass Cat { constructor(name) { this.name = name; } pet() { console.log(this.name + ' purrs.'); } }
const louis = new Cat('Louis'); louis.pet(); const spyspy = new Cat('SpySpy'); // I know which cat this is right away spyspy.pet(); // ACHTUNG: this will NOT fail louis.name = 'Some Other Kitty'; // ... But this will fail louis = new Cat('Louis');
for...of
for friendly loopsi
am so tired of thisvar cats = [ 'Louis', 'SpySpy', 'Pyro' ]; // In the bad old days var i; for (i = 0; (i < cats.length); i++) { console.log(cats[i]); } // IE11: the functional way is not terrible cats.forEach(function(cat) { console.log(cat); });
for...of
for friendly loopsfor ... of
is friendly and safeconst cats = [ 'Louis', 'SpySpy', 'Pyro' ]; for (const cat of cats) { console.log(cat); }
function Cat(name) { this.name = name; } Cat.prototype.pet = function() { console.log(this.name + ' purrs.'); } const louis = new Cat('Louis'); louis.pet();
class Cat { constructor(name) { this.name = name; } pet() { console.log(this.name + ' purrs.'); } } const louis = new Cat('Louis'); louis.pet();
const cats = [ 'Louis', 'SpySpy' ]; cats.forEach(function(cat) { console.log(`${cat} is a fine cat.`); });
const cats = [ 'Louis', 'SpySpy' ]; cats.forEach(cat => console.log(`${cat} is a fine cat.`));
cat
so muchcat
const rateLimit = 2; let pending = 0; function api(action, data) { // Asynchronous, like a real API call return new Promise((resolve, reject) => { if (pending >= rateLimit) { return reject('RATE LIMITED'); } pending++; setTimeout(() => { let reversed = ''; for (let i = (data.cat.length - 1); (i >= 0); i--) { reversed += data.cat.charAt(i); } pending--; resolve(reversed); }, 500); }); }
async function go() { const cat = 'Louis'; console.log(await api('reverse', { cat: cat })); } go();
cat
so muchcat
is nice, and will sufficeconst rateLimit = 2; let pending = 0; function api(action, data) { // Asynchronous, like a real API call return new Promise((resolve, reject) => { if (pending >= rateLimit) { return reject('RATE LIMITED'); } pending++; setTimeout(() => { let reversed = ''; for (let i = (data.cat.length - 1); (i >= 0); i--) { reversed += data.cat.charAt(i); } pending--; resolve(reversed); }, 500); }); }
async function go() { const cat = 'Louis'; console.log(await api('reverse', { cat })); } go();
...
: give it a rest!arguments
: mmm, crunchy boilerplatefunction send(recipient, message /* , message2, message3... */) { var messages = Array.prototype.slice.call(arguments, 1); console.log(recipient + ', you have the following messages: ' + messages.join(', ') ); } send('Louis', 'meow', 'screech', 'purr');
...
: give it a rest!function send(recipient, ...messages) { console.log(recipient + ', you have the following messages: ' + messages.join(', ') ); } send('Louis', 'meow', 'screech', 'purr');
`
: template strings are beautifulblah + blech + more + blah
: quit typing ticksfunction send(recipient, ...messages) { const joined = messages.join(', '); console.log(recipient + ', you have the following messages: ' + joined); } send('Louis', 'meow', 'screech', 'purr');
`
: template strings are beautiful${love}
backticksfunction send(recipient, ...messages) { const joined = messages.join(', '); console.log(`${recipient}, you have the following messages: ${joined}`); } send('Louis', 'meow', 'screech', 'purr');
...
returns: the object spread operatorfunction api(data) { // Make a new object, don't modify the input! const _data = { cat: data.cat || 'Louis', // Watch out for 0! priority: (data.priority !== undefined) ? data.priority : 1 }; console.log('Our API would receive:', _data); } api({}); api({ cat: 'SpySpy' }); api({ cat: 'Felix', priority: 0 }); api({ cat: 'Sammy', priority: 100000 });
...
returns: the object spread operator...
spreads like butterfunction api(data) { const _data = { cat: 'Louis', priority: 1, // Pull in all the properties of data ...data }; console.log('Our API would receive:', _data); } api({}); api({ cat: 'SpySpy' }); api({ cat: 'Felix', priority: 0 }); api({ cat: 'Sammy', priority: 100000 });
function getCatInfo() { return { name: 'Louis', age: 3, pets: 5000000000 }; } const info = getCatInfo(); console.log(info.name);
function getCatInfo() { return { name: 'Louis', age: 3, pets: 5000000000 }; } const { name } = getCatInfo(); console.log(name);
// Long comment documenting the options usually goes here function send(recipient, options, ...messages) { if (options.style === 'terse') { console.log(`${recipient}: ${messages.join(', ')}`); } else { console.log(`${recipient}: ${messages.join('\r\n')}`); } } send('Louis', { style: 'terse' }, 'Felix hissed at you', 'SpySpy hissed at you' );
function send(recipient, { style }, ...messages) { if (style === 'terse') { console.log(`${recipient}: ${messages.join(', ')}`); } else { console.log(`${recipient}: ${messages.join('\r\n')}`); } } send('Louis', { style: 'terse' }, 'Felix hissed at you', 'SpySpy hissed at you');
function speak(message) { if (message === undefined) { message = 'miaow'; } console.log(message); } speak(); speak('hisssss');
function speak(message = 'miaow') { console.log(message); } speak(); speak('hisssss');
const messages = [ { speaker: { name: 'Joe Smith', job: { title: 'Dentist' } } }, { speaker: { name: 'Jane Doe' } } ]; messages.forEach(function(message) { console.log(`Job Title: ${message.speaker && message.speaker.job && message.speaker.job.title}`); });
const messages = [ { speaker: { name: 'Joe Smith', job: { title: 'Dentist' } } }, { speaker: { name: 'Jane Doe' } } ]; for (const message of messages) { console.log(`Job Title: ${message?.speaker?.job?.title}`); }
This one is more recent
var data = { name: 'Louis', handsomeness: '10', orange: true }; Object.keys(data).forEach(function(key) { console.log(key + ': ' + data[key]); });
const data = { name: 'Louis', handsomeness: '10', orange: true }; for (const [ key, value ] of Object.entries(data)) { console.log(`${key}: ${value}`); }
var data = [ 'Louis', 'SpySpy', 'Pyro', 'Louis' ]; console.log('Before: ' + data.join(', ')); var newData = []; // OK in IE11 data.forEach(function(datum) { if (newData.indexOf(datum) === -1) { newData.push(datum); } }); console.log('After: ' + newData.join(', ')); // Who am I kidding, you'd probably use lodash
const data = [ 'Louis', 'SpySpy', 'Pyro', 'Louis' ]; console.log('Before: ' + data.join(', ')); let newData = [ ... new Set(data) ]; console.log('After: ' + newData.join(', ')); // Bye-bye lodash (for most use cases)
function api(action, data) { // Asynchronous, like a real API call return new Promise((resolve, reject) => { setTimeout(() => { let reversed = ''; for (let i = (data.cat.length - 1); (i >= 0); i--) { reversed += data.cat.charAt(i); } resolve(reversed); }, 500); }); }
// Reversing cats is very hard work, // must call special remote API api('reverse', { cat: 'Louis' }).then(function(reversed) { console.log(reversed); return api('reverse', { cat: 'SpySpy' }); }).then(function(reversed) { console.log(reversed); return api('reverse', { cat: 'Sammy' }); }).then(function(reversed) { console.log(reversed); }).catch(function(err) { // Call the vet });
function api(action, data) { // Asynchronous, like a real API call return new Promise((resolve, reject) => { setTimeout(() => { let reversed = ''; for (let i = (data.cat.length - 1); (i >= 0); i--) { reversed += data.cat.charAt(i); } resolve(reversed); }, 500); }); }
async function go() { console.log(await api('reverse', { cat: 'Louis' })); console.log(await api('reverse', { cat: 'SpySpy' })); console.log(await api('reverse', { cat: 'Sammy' })); } go();
async/await
: meet for...
of, your new BFFconst rateLimit = 2; let pending = 0; function api(action, data) { // Asynchronous, like a real API call return new Promise((resolve, reject) => { if (pending >= rateLimit) { return reject('RATE LIMITED'); } pending++; setTimeout(() => { let reversed = ''; for (let i = (data.cat.length - 1); (i >= 0); i--) { reversed += data.cat.charAt(i); } pending--; resolve(reversed); }, 500); }); }
// works const twoCats = [ 'Louis', 'SpySpy' ]; Promise.all( twoCats.map(cat => api('reverse', { cat: cat })) ).then(reversed => { console.log(reversed); // hits the rate limit const threeCats = [ 'Louis', 'SpySpy', 'Sammy' ]; return Promise.all( threeCats.map(cat => api('reverse', { cat: cat })) ); }).then(reversed => { console.log(reversed); }).catch(e => { console.log(`Call the vet! ${e}`); });
async/await
: meet for...
of, your new BFFfor...of
const rateLimit = 2; let pending = 0; function api(action, data) { // Asynchronous, like a real API call return new Promise((resolve, reject) => { if (pending >= rateLimit) { return reject('RATE LIMITED'); } pending++; setTimeout(() => { let reversed = ''; for (let i = (data.cat.length - 1); (i >= 0); i--) { reversed += data.cat.charAt(i); } pending--; resolve(reversed); }, 500); }); }
async function go() { const threeCats = [ 'Louis', 'SpySpy', 'Sammy' ]; // Note: this trick does not work with forEach for (const cat of threeCats) { // OMG SO... NORMAL console.log(await api('reverse', { cat: cat })); } } go();