Skip to content

Instantly share code, notes, and snippets.

@slaughtr
Created June 20, 2018 21:24
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save slaughtr/9e4356ef2ce900236c192adf878edb21 to your computer and use it in GitHub Desktop.
Save slaughtr/9e4356ef2ce900236c192adf878edb21 to your computer and use it in GitHub Desktop.
es6
// I cannot absolutely guarantee I got everything completely correct, but I'm 95% sure I got at least 95% of it correct
// -----------------
// --- PROMISES
// -----------------
// Callback hell:
doSomething(params, (err, data) => {
if (err) console.error(err)
else {
somethingElse(params, (err, data) => {
// you can see where this is going
// and good luck with error handling
})
}
})
// Promises
const doSomething = params => {
return new Proimse((resolve, reject) => {
const data = process(params)
resolve(data) //
})
}
doSomething(params)
.then(data => somethingElse)
.catch(err => console.error)
// Parallel, you say?
const arr = ['lots', 'of', 'items', ...]
Promise.all(arr.map(doSomething)).then(resultArr => somethingElse)
// -----------------
// --- LET & CONST
// -----------------
// Bad syntax on purpose, I was avoiding using let/var/const in function declarations
// var
function() {
var inTheGlobalSpace = 'No matter where you put it'
var inTheGlobalSpace = `So you can't re-use the variable name safely`
}
var inTheGlobalSpace = 'Now everything might break'
// let
function() {
let thisExistHere = 'And not outside the current context'
let thisExistHere = 'You cannot re-declare me.' // SyntaxError: Identifier 'thisExistHere' has already been declared
thisExistHere = 'But you can reassign me!'
}
console.log(thisExistsHere) // ReferenceError: thisExistsHere is not defined
// const
const notReassignable = 'Once set, you cannot re-assign this variable'
notReassignable = 'This will not work' // TypeError: Assignment to a constant variable
// -----------------
// --- ARROW FUNCTIONS
// -----------------
// The old way
function operator() {
return `Did you know it's called the function operator?`
}
// Younger, stronger, smarter
operator = () => 'Empty parenthesis with no arguments.'
// Not convinced? Check this out
// Bloated, busy, gross
const iChooseYou = team.map(function(pokémon) {
return pokémon.name
}).filter(function(name) {
return name === 'Charizard'
})
// Clean, readable, slick
const iChooseYou = team.map(pokémon => pokémon.name).filter(name => name === 'Charizard')
// Easy return of objects, without fear of ASI ruining your day
const decorate = team.map(pokémon => ({
oldName: pokémon.name,
newName: pokémon.name.toUpperCase(),
status: 'AWESOME'
}))
// The scope of 'this' is lexically bound with arrow functions. They do not have their own 'this', and use the 'this' of their immediate scope
// -----------------
// --- FOR...OF
// -----------------
// Just the best
const starTrek = [ 'TOS', 'TNG', 'VOY', 'DS9', 'ENT', 'DSC']
// Better than a for loop, but...
Promise.all(starTrek.map(series => {
return new Promise(resolve => {
myDatabase.update({Table: 'best_shows', Item: series}).then(resolve)
})
}))
// We can make it better (another one in async/await)
for (const series of starTrek) {
myDatabase.update({Table: 'best_shows', Item: series}).then(() => null)
}
// Utilize break (another one in async/await)
for (const series of starTrek) {
netflix.search({Show: series}).then(hasIt => {
if (hasIt === true) {
startNetflixShow(series)
break;
}
})
}
// Without break
Promise.all(starTrek.map(series => {
return new Promise(resolve => {
netflix.search({Show: series}).then(hasIt => {
if (hasIt === true) resolve(series)
else resolve(false)
})
})
})).then(resultArray => startNetflixShow(resultArray.find(item => item))) // item will be truthy if resolve(series) occurred
// Works wonderfully with objects
const captains = {
TOS: 'Kirk',
TNG: 'Picard',
VOY: 'Janeway',
DS9: 'Sisko',
ENT: 'Archer',
DSC: 'Pike',
WeWereRobbed: 'Worf'
};
for (const show of Object.keys(captains)) {
console.log(`${captains[show]} was the captain of ${show}`);
// Kirk was the captain of TOS, Picard was the captain of TNG, Janeway was the...
}
// for...in has caveats
// maybe a library somewhere in your dependencies did this:
Object.prototype.messUpYourCode = 'Some thing';
for (const show in captains) {
console.log(`${captains[show]} was the captain of ${show}`);
// ..., Worf was the captain of WeWereRobbed, ​​​​​Some thing was the captain of messUpYourCode
}
// However, this can be avoided by adding if (captains.hasOwnProperty(show)) {} around everything
for (const show in captains) {
if (captains.hasOwnProperty(show)) {
console.log(`${captains[show]} was the captain of ${show}`);
// ..., Pike was the captain of DSC, Worf was the captain of WeWereRobbed
}
}
// Arrays are not guaranteed to iterate in order with for...in, so be careful if that's important in your code
// The variable in a for...in on an array is the index of the item
// you CAN do single-line for...in and for...of statements
for (const index in ['a','b','c']) console.log(index) // 0, 1, 2
// -----------------
// --- DESTRUCTURING, REST, AND SPREAD
// -----------------
// Object desctructuring is great for passing data around
// real-world example!
exports.handler = async function(event, context, callback) {
// get params from API call
const { client, project, folder, user, batch, test, url } = event.query;
if (client && project && sub && user && batch && test && url) {
logger.debug(`Params: client = ${client} | project = ${project} | sub = ${sub} | user = ${user} | batch = ${batch} | test = ${test} | url = ${url}`);
const lambdaParams = {
FunctionName: 'acknowledgeAlert',
// Event should allow async execution (the api call will return ~immediately)
InvocationType: 'Event',
// we don't want the logs in this function
LogType: 'None',
// it's looking for userId, clientId, batchId, project, sub, testId, url
Payload: JSON.stringify({
clientId: client,
project: project,
folder: folder,
userId: user,
testId: test,
url: url,
batchId: batch
})
};
try {
await lambda.invoke(lambdaParams).promise();
logger.info('Invoked lambda');
context.succeed();
} catch (error) {
logger.error(error);
context.fail();
}
} else {
logger.error('Bad params');
context.fail();
}
};
// Imagine we get these results from an API call
// {
// 's01e01': 'Drunk people',
// 's01e02': 'Angry drunk people',
// ...
// 's68e37': 'Too drunk to be angry',
// 's69e38': 'This is not a good joke'
// ...
// }
// Destructuring assignment
const { s01e01, s01e02 } = result
console.log(s01e01, s01e02) // Drunk people Angry drunk people
// Rest operator
const { s01e01, s01e02, ...rest } = result
console.log(rest) // { s01e03: 'Another one',...,s68e37: 'Too drunk to be angry',...etc}
// Also works with arrays
const [ first, second, ...rest ] = [1,2,3,4,5,6,7] // rest = [3,4,5,6,7]
// And the opposite of rest, the Spread operator
// Concat arrays
const moreNumbers = [ ...[1,2,3,4,5,6], ...rest] // [1,2,3,4,5,6,3,4,5,6,7]
// Clone arrays
const cloneWithoutModifyingOriginal = [...moreNumbers]
// Clone objects
const mutableVariable = {...dog, ...cat}
// Renaming properties
const obj = { animal: { dog: `Seymour` }, place: `Too far away` }
// We can't help but call dogs puppers, right?
const { animal: { dog: pupper }, place: where } = obj // (can be confusing syntax here)
// Replaces
const pupper = obj.animal.dog // ‘Seymour`
const where = obj.place // ‘Too far away`
// -----------------
// --- SETS
// -----------------
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
// De-duplicating an array
// The standard way, usually. Nothing wrong with it, it's short enough.
// returns true for only the first instance of item
const unique = duplicates.filter((item, index) => duplicates.indexOf(item) === index)
// With Sets
const unique = [...new Set(duplicates)]
// Note this works because of the spread operator, as Sets are not Arrays, but they are iterables
// or, with Array.from()
const unique = Array.from(duplicates) // not as fun :(
// Practical use
const dreamJobs = new Set()
emitter.on('upload', item => dreamJobs.add(item))
emitter.emit('upload', 'Tyrannosaurus Rex') // dreamJobs = Set ['Tyrannosaurus Rex']
emitter.emit('upload', 'Tyrannosaurus Rex') // nothing added, dreamJobs = Set ['Tyrannosaurus Rex']
emitter.emit('upload', 'Brontosaurus') // dreamJobs = Set ['Tyrannosaurus Rex', 'Brontosaurus']
await processItems(dreamJobs)
dreamJobs.clear() // dreamJobs.size = 0
emitter.emit('upload', 'Tyrannosaurus Rex') // dreamJobs = Set ['Tyrannosaurus Rex']
// Iteration with sets
// Set().forEach() , or for...of by using Set.keys() or Set.values() - both do the same thing...as does Set.entries(), though it returns each value as [val, val]
const dreamJobs = new Set(['Tyrannosaurus Rex', 'Brontosaurus'])
dreamJobs.forEach(job => console.log) // 'Tyrannosaurus Rex', 'Brontosaurus'
for (const job in dreamJobs.values() /* or dreamJobs.keys() */) console.log(job) // 'Tyrannosaurus Rex', 'Brontosaurus'
for (const job in dreamJobs.entries() ) console.log(job) // ['Tyrannosaurus Rex', 'Tyrannosaurus Rex',], ['Brontosaurus', 'Brontosaurus']
// destructuring assignment with array
for (const [a, b] in dreamJobs.entries() ) console.log(a) // 'Tyrannosaurus Rex', 'Brontosaurus'
// Sets use different methods than arrays: Set.size(), Set.has(value), Set.add(value), Set.delete(value), and Set.clear()
dreamJobs.size() // 2
dreamJobs.has('Tyrannosaurus Rex') // true
dreamJobs.has('Shark') // false
dreamJobs.add('Shark') // dreamJobs = Set ['Tyrannosaurus Rex', 'Brontosaurus', 'Shark']
dreamJobs.delete('Shark') // dreamJobs = Set ['Tyrannosaurus Rex', 'Brontosaurus']
dreamJobs.clear() // dreamJobs = Set {}
dreamJobs.size() // 0
// -----------------
// --- MODULES
// -----------------
// You can use require() statements inline
const rarelyCalledFunction = params => {
require('fs').writeFileSync('./run-without-error.json', params)
}
// You can also call functions using require() and an IIFE (immediately invoked function expression)
// say-hi.js
console.log('Hi Mom')
// index.js
(() => require('./say-hi.js'))() // console logs 'Hi Mom'
// You can use destructuring with imports
// An entire library imported...for one module
import * as React from 'React'
React.Component.doStuff()
// Destructuring, only the component you need
import { Component } from 'React'
Component.doStuff()
// Or rename it
import { Component as RC } from 'React'
RC.doStuff()
// Sharing resources can be very helpful but can also lead to issues
// In Node, all files at the same directory level/same module share instances
// Especially useful for database connections, as they take ~20x longer to start than the rest of your code
// ./app/db.js
module.exports.writeToTable = db.client.write(msg)
// ./app/donger.js
const { writeToTable } = require('db')
writeToTable('ლ(́◉◞౪◟◉‵ლ)')
// ./app/emoji.js
const { writeToTable } = require('db') // same instance of db.js so the client will still be loaded
writeToTable('🤷‍')
// With Import, it depends on whether a class or an instance is exported
// Singleton, so shared instance
class Settings {}
export let singleton = new Settings()
// Not instantiated, so can't be singleton
export class Settings {}
// Other files have to instantiate with new
const settings = new Settings()
// -----------------
// --- ETC
// -----------------
// Arrays
const setToArray = Array.from(someSet) // Creates a clone
const unicode = Array.from('▲ESTHETIC') // ['▲','E','S','T','H','E','T','I','C']
// Array.find() - Returns the first item that meets a criteria
const seven = [1,3,7,9,11,13,17,21].find(num => num % 7 === 0) // 7
// Strings
// String.includes(), startsWith(), endsWith()
const carGoes = 'VROOM'
console.log(carGoes.includes('OO')) // true
console.log(carGoes.startsWith('VR')) // true
console.log(carGoes.endsWith('M')) // true
// Numbers
// JavaScript is infamous for mishandling floats when doing comparisons
// Number.EPSILON - allows comparing floats
const lolFloats = Math.abs(0.2 - 0.3 + 0.1); // you'd think 0
console.log(lolFloats === 0) // false
console.log(lolFloats); // expected output: 2.7755575615628914e-17 aka 0.000000000000000027755575615628914
console.log(lolFloats < Number.EPSILON); // true
// Number.isNaN() - actually does what you’d think
isNaN('123ABC'); // true: parseInt("123ABC") is 123 but Number("123ABC") is NaN
Number.isNaN('123ABC') // false
isNaN(''); // false: the empty string is converted to 0 which is not NaN
Number.isNaN('') // true
// Quick type coercion to Number from string, great for API results
const num = '42'
console.log(23 + +num) // the + attatched to the variable coerces it into a number
console.log(typeof +'12345') // number
// Objects
// Spread operator isn't always available. Object.assign() uses get() and set() so may be preferable in some situations
// Object.assign() – shallow clone
const newObj = Object.assign({a: 1, b: 2}, {})
// newObj = {a: 1, b: 2}
// Concat objects
const newObj = Object.assign({a: 1, b: 2}, {c: 3, d: 4})
// newObj = { a: 1, b: 2, c: 3, d: 4 }
// To do a deep clone (clone all properties, no references to original object in memory)
const newObj = JSON.parse(JSON.stringify(oldObj))
// Template literals
// Template literals are strings that allow inline variables.
//They are created with backticks: `
console.log(typeof ``) // string
// Variables are used with ${}
console.log(`Got status code: ${response.statusCode}, headers: ${response.headers}`)
// Multi-line supported
let multiLine = `Lorem ipsum dolor sit amet,
consectetur adipiscing elit.
Maecenas varius tempor arcu,
quis hendrerit nunc accumsan quis.`
// Default Parameters
// Default parameters are used if no value is passed, or the value is undefined
// Just your normal rectangle factory
const createRectangle = (w, h, color) => ({width: w, height: h, color, area: w*h})
// If we provide all the parameters, no problem!
console.log(createRectangle(1, 1, 'red')) // { width: 1, height: 1, color: 'red', area: 1 }​​​​​
// What if x is undefined
let x = undefined
console.log(createRectangle(1, x, 'green')) // { width: 1, height: undefined, color: 'green', area: NaN }​​​​​
// Default parameters to the rescue
const createRectangle = (w = 5, h = 5, color = 'blue') => ({width: w, height: h, color, area: w*h})
// Now x being undefined doesn't break anything
console.log(createRectangle(1, x, 'green')) // { width: 1, height: 5, color: 'green', area: 5 }​​​​​
// And we don't have to pass any parameters at all if we don't want to
console.log(createRectangle()) // { width: 1, height: 5, color: 'blue', area: 25 }​​​​​
// Default parameters also work with destructuring assignments
const obj = { a: 1, b: 'buzz'}
// Now we don't get c at all
const { a, b, c } = obj // obj = { a: 1, b: 'buzz'}
// With default
const { a, b, c = true } = obj // obj = { a: 1, b: 'buzz', c: true }
// Ternaries & Logical Operators
// Shortened if statements, more or less . Format is: condition ? true : false
1 > 0 ? console.log('This will print') : console.log('This will not')
// Ternaries and || in assignment and params
// If all items are falsey, .find() will return undefined, and the default of 'Planet Earth II' will be used instead
startNetflixShow(resultArray.find(item => item) || 'Planet Earth II')
// Handy when building objects for API calls or database inserts (DynamoDB, for instance, won't accept null values)
const obj = {
prop: someVar,
// if otherVar is falsey (undefined, null, 0, false, '', etc), it will default to 'NOT FOUND'
another: otherVar || 'NOT FOUND'
}
// Ternaries allow slightly more logic, at the expense of readability
// If the db was updated within the last hour, data will be what's already in the app object, otherwise get data from the database
const data = db.lastUpdated('hours', 'ago') > 1 ? db.getData() : app.data
// You can nest ternaries indefinitely, but you shouldn't:
const ohDear = (data[0] == '-1') ? 4 : ((data[0] == '0') ? 5 : (data[0] > 0 ? 3 : 2))
// Equivalent if statements. More lines, but much more readable
let ohDear
if (data[0] === -1) ohDear = 4
else if (data[0] === 0) ohDear = 5
else if (data[0] > 0) ohDear = 3
else ohDear = 2
// -----------------
// --- ASYNC/AWAIT
// -----------------
// async/await is syntactic sugar around Promises
// async functions ALWAYS return a Promise, await REQUIRES a Promise
const getData = async function(id) {
return await db.getItem(id)
return db.getItem(id) // no await needed, function returns a Promise
}
const getData = async id => db.getItem(id) // Arrow function implicit return works, too!
// await is only available inside async functions
const data = await getData(143) // invalid
const run = async () => {
const data = await getData(143) // valid, since inside async function
// do stuff with data
}
// Since async functions return Promises, they are thenable
run().then(result => db.write(result))
// But what about error handling? try/catch to the rescue
let data
try {
data = await getData(143) // database has no entry for 143
} catch (e) {
console.error(e) // does not crash
data.error = e
}
// Considerably cleaner code
// We can make it better even more! await works in some loops (for, for...of, for...in) without config
for (const series of starTrek) {
await myDatabase.update({Table: 'best_shows', Item: series})
}
// Utilize break (more better with await)
for (const series of starTrek) {
const hasIt = await netflix.search({Show: series})
if (hasIt === true) {
startNetflixShow(series)
break;
}
}
// -----------------
// --- es7
// -----------------
// Array.includes()
// Returns true/false if item is in array
const arr = [1,2,3]
console.log(arr.includes(1)) // true
console.log(arr.includes(77)) // false
// Replaces this
console.log(arr.indexOf(1) > -1) // true
// Exponentiation Operator - **
// Shorthand for Math.pow()
const four = 2**2
console.log(four) // 4
// Replaces this
const four = Math.pow(2, 2)
console.log(four) // 4
// try/catch bubbling
// Errors bubble up to the nearest catch, no further
try {
try {
await writeNoErrors()
} catch (err) {
console.error(err) // LogicError: There will always be rrors
}
} catch (error) {
console.error(error) // Never called, as the error was caught in the previous catch
}
// You can force the error to bubble up
try {
try {
await writeNoErrors()
} catch (err) {
if (err === 'Typo') throw new Error(err)
}
} catch (error) {
console.error(error) // Called with the thrown err
}
// finally
// finally is strange, and in general not what you want
try {
return 'Try'
} catch (err) {
return 'Catch'
} finally {
return 'Finally' // this will ALWAYS be the return value of this try/catch/finally block
}
// ....maybe more to come
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment