Enough Modern JavaScript

Also checkout how to do things with Vanilla JS: </dom>.

The return value of function must use return:

function identity(x) {
  return x
}

const object = {a: 1, b: 2, c: 3, d: 4}

object.a
1

object['a']
1

'use strict' turn on the Stricit mode.

Debug with debugger, console.log(), console.trace().

How did JavaScript’s console.log get its name?

const name = 'Juanito'
const loginCounts = {[name]: 5}

loginCounts.Juanito
5
const users = [
  { name: 'Juanito', age: 15 },
  { name: 'Bella', age: 16 },
]

function age(user) {
  return { [user.name]: user.age }
}

age(users[0])

const name = 'Juanito'
const age = 36
const user = {name, age};

[user.name, user.age]
['Juanito', 36]

const user = {
  get name() { return 'Juanito' }
}

user.name
'Juanito'

const user = {
  realName: 'Juan',
  set name(newName) { this.realName = newName }
}

user.name = 'Juanito'
user.realName
'Juanito'

Don’t use var, use let (can change) and const (cannot change).

On let vs const.

ESLint rule: Require Guarding for-in (guard-for-in)

for (.. in ..) {} iterates over keys in array.

Skips undefined and null elements.

const keys = []
const letters = []
letters[1] = 'a'
for (const key in letters) {
  keys.push(key)
}

keys
['1']

for (.. of ..) {} iterates over values in array.

Returns undefined for undefined or null elements.

const keys = []
const letters = []
letters[1] = 'a'
for (const key of letters) {
  keys.push(key)
}

keys
[undefined, '1']

forEach method will skip over empty slots.

var users = [
  {name: 'Lia'},
  {name: 'Yejin'},
]
var names = []
users.forEach(user => {
  names.push(user.name)
})
names

'str'
"str"
`str` // called template literals

Template literals can do interpolation:

`1+1=${1+1}`

${1+1} calls toString() on computed value.

template literals can have newline:

const signoff = `
  Best,
  Juanito
`

dedent can remove leading whitespaces.

[1, 2].toString()
'1,2'


({a: 1}).toString()
'[object Object]'

Example of sum function:

function sum(...numbers) {
  return numbers.reduce((x, y) => x + y, 0)
}

Last resort: Use bind to set a function’s this.

... is called spread operator.

Must come last and only used once in function signature, comes after positional arguments.

f(...args) {

}

Pass in rest args to function

function add(x, y) {
  return x+y
}

const numbers = [1, 9]
add(...numbers)

The * says numbers function is a generator function.

function* numbers() {
  yield 1
  yield 2
}

for (const n of numbers) {
  console.log(n)
}

Array.from(numbers)
=> [1, 2]

function hello(){
  return 'Hello'
}

hello`world!`
=> `Hello`

To deconstruct literal and interpolated values from string:

function extractArguments(strings, ...values) {
  return {
    first: strings,
    second: values,
  }
}

extractArguments`1${2}3`
{ first: ['1', '3'], second: [2] }

Array’s type is 'object' instead of 'array'.

const obj = {a: 1, b: 2}
const keys = []
for (const key in obj) {
  keys.push(key)
}

typeof keys
=> 'object'

Array looks like an implcity object where key is the index:

Object.keys(['a', 'b', 'c'])
['0', '1', '2']

const letters = ['a', 'b']
const [a, b] = letters

// Skip value
const letters = ['a', 'b', 'c']
const [a, , c] = letters
['a', 'c']

// Supply value when missing
const letters = ['a', 'b']
const [a, b, c='C'] = letters
['a', 'b', 'C']

// Deconstruct over Supplied value
const letters = ['a', 'b', 'c']
const [a, b, c='C'] = letters
['a', 'b', 'c']

// String can also be deconstructed
const letters = 'xy'
const [x, y] = letters
x
// => 'x'

some always returns false for an empty array.

[].some(x => x > 0)
true

[10, 20].some(x => x > 10)
false

every always returns true for an empty array.

[].every(x => x > 0)
true

[10, 20].every(x => x > 10)
false

There is no none function in JavaScript. we can implement like so:

!arr.some(f)

![2,3,4].some(number => number === 1)

JSON.stringify— A key with undefined value will be removed from resulting JSON

JSON.stringify(obj, f). JSON.stringify preserves key order.

JSON.parseparse will look for a toJSON key in object

class Animal {
  constructor(name) {
    this.name = 'name'
  }
}

new Animal().name

Methods on class is called Static Methods.

class User {
  static getUserName(user) {
    return user.name
  }
}

class Person extends Animal {
  constructor() {
    super(name + ' the human')
  }
}

const names = new Set([1,1,2])
names.add()
names.delete()
names.clear()
function benchmark(f) {
  const start = new Date().getTime()
  for (let i=0; i<1000000; i++) {
    f()
  }
  const end = new Date().getTime()
  return end - start
}

class User {
  constructor(name, email) {
    this.name = name
    this.email = email
    this.admin = false
  }
}

class Admin extends User {
  constructor(name, email) {
  super(name, email)
  this.admin = true
  }
}

const admin = new Admin('Juanito', '[email protected]', true)

admin.admin
=> true

admin instanceof User
=> true

Class will capture surrounding function’s name

function createUser(name) {
  class User {
    constructor() {
      this.name = name
    }
  }
  return new User()
}

createUserr('Guest').name

const user = {
  'user name'() { return 'Juanito' }
}

user['user name']()
=> 'Juanito'

const set1 = new Set([1, 2, 3])
const set2 = new Set([2, 3, 4])
const unionSet = new Set([...set1, ...set2]);
[unionSet.has(1), unionSet.has(4)]

Array.from(unionSet)
[1, 2, 3, 4]

const set1 = new Set([1, 2, 3])
const set2 = new Set([2, 3, 4])
const intersectionSet = new Set(
  Array.from(set1).filter(n => set2.has(n))
);

Array.from(intersectionSet)
[2, 3]

const set1 = new Set([1, 2, 3])
const set2 = new Set([2, 3, 4])
const differenceSet = new Set(
  Array.from(set1).filter(n => !set2.has(n))
);

Array.from(differenceSet)
[1]