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 varvar 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 varlet: so scope-y fresh
let 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 think
class 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 & safe
class 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 this
var 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 safe
const 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 suffice
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 }));
}
go();
...: give it a rest!arguments: mmm, crunchy boilerplate
function 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 ticks
function 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} backticks
function 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 operator
function 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 butter
function 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 BFF
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);
});
}
// 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();