JS today

A (re-)introduction to JavaScript

Maxence POUTORD

Evolution of JS

Reminder

Data types

  • Integer
  • Float
  • String
  • Boolean
  • Object
  • Function
  • Array
  • null
  • undefined

Data types in JS

  • Number
  • String
  • Boolean
  • Object
    • Function
    • Array
    • Date
    • RegExp
  • null
  • undefined
  • Symbol (new in ES2015)

Assigning a variable

var, let and const

assign variable to identifierreassign variablereassign identifier
var
let
const

const !== immutable


const hero = {
  name: 'The Punisher'
}

hero.name: 'Daredevil'

console.log(hero) // Object {name: "Daredevil"}
      

That not a reason not to use it!

let&const are block scoped


var x = 1
if (true) {
  var x = 2  // same variable as above
  console.log(x)  // 2
}
console.log(x)  // 2
      

let x = 1
if (true) {
  let x = 2  // different variable
  console.log(x)  // 2
}
console.log(x)  // 1
      

let&const are also intemporal dead zone

Function

First class citizen

In JS a function can be passed just like any other value


const hero = {
  name: 'The Punisher',
  shoot: function () {
    return 'bang bang'
  }
}

hero.name // 'The Punisher'
hero.shoot // function () { return 'bang bang }
hero.shoot() // 'bang bang'
      

Closures


const counter = (function () {
  let privateCounter = 0
  function changeBy (val) {
    privateCounter += val
  }
  return {
    increment: function () {
      changeBy(1)
    },
    decrement: function () {
      changeBy(-1)
    },
    value: function () {
      return privateCounter
    }
  }
})()
      

counter.increment()
counter.increment()
counter.value() // 2
      

Arrow function

shorter syntax


function (a, b) {
  return a + b
}

(a, b) => {
  return a + b
}

(a, b) => a + b
      

Doesn't redefine: this, arguments, super and new.target

Bye bye "var self = this"!

Arrow functions


var fruits = ["apple", "banana", "kiwi"]
      

ES5:


var fruitsUpperCase = fruits.map(function (f) {
  return f.toUpperCase()
})
      

ES2015+:


const fruitsUpperCase = fruits.map(f => {
  return f.toUpperCase()
})
      
// or better:
const fruitsUpperCase = fruit.map(f => f.toUpperCase())
      

scope and this

Default value: the global object


// in a Browsers
this === window

// in Node
this === global
        

or undefined in strict mode

Values of this


// inside function
function isWindow () {
  return this === window 
}
isWindow() // true
        

// inside object
var Hero = {
  name: 'The Punisher',
  isHero: function () {
    return this === Hero
  }
}
Hero.isHero() // true
        

this depends on the context


var Hero = {
  name: 'The Punisher',
  describe: function () {
    return 'Im '+ this.name
  }
}
var name = 'bob'
var punisher = Hero.describe
punisher() // Im bob
          

Pitfall: this in an inner function


var numbers = {  
   numberA: 5,
   numberB: 10,
   sum: function () {
     console.log(this) // which context?
     function calculate () {
       console.log(this) // which context?
       return this.numberA + this.numberB
     }
     return calculate()
   }
}
numbers.sum() // result?
        

a solution: Function.prototype.call()

Yes, functions have methods in JS


var numbers = {  
   numberA: 5,
   numberB: 10,
   sum: function () {
     console.log(this === numbers) // true
     function calculate() {
       console.log(this === numbers) // true
       return this.numberA + this.numberB
     }
     return calculate.call(this)
   }
}
numbers.sum()
        

See also: Function.prototype.apply()

classical vs prototypal inheritance

In a nutshell

  • A class is a blueprint
  • A prototype is an object instance.

Prototypal OOP

  • Each object is based on another object
  • Each function is potentialy a constructor

function Hero (options) {
  this.name = options.name
  this.nickname = options.nickname
  this.family = options.family
}

const logan = new Hero({
  name: 'Logan',
  nickname: 'Wolverine',
  family: 'Marvel'
})
      

Method inside constructor


function Hero (name) {
  this.name = name

  this.describe = function () {
    return 'I\'m ' + this.name
  }
}

const groot = new Hero('Groot')
groot.describe() // "I'm Groot"
      

Each instance redefine his own method ๐Ÿ‘Ž

Method outside constructor


function Hero (name) {
  this.name = name
}

Hero.prototype.describe = function () {
  return 'I\'m ' + this.name
}

const groot = new Hero('Groot')
groot.describe() // "I'm Groot"
      

Instances share the same method ๐Ÿ‘

Class (ES2015)


class Hero {
  constructor (name) {
    this.name = name
  }

  describe () {
    return 'I\'m ' + this.name
  }
}

const groot = new Hero('Groot')
groot.describe() // "I'm Groot"
      

How to create private attribute?

Well, you can't :)

It's a fake!


typeof Hero // function
      

===


function Hero (name) {
  this.name = name
}

Hero.prototype.describe = function () {
  return 'I\'m ' + this.name
}
      

class are NOT like OO classes (= syntactic sugar)

Inheritance

Composition over inheritance

Asynchronous

Synchronous vs Asynchronous

JS is Asynchronous


function ajaxBurgers (url, callback) {
  const fakeDelay = Math.random() * 1500
  const fakeResponse = ['๐Ÿ”', '๐ŸŒฎ', '๐ŸŒญ']
  setTimeout(() => {
    callback(fakeResponse)
  }, fakeDelay)
}

const menu = {}
menu.drink = '๐Ÿน'
menu.side = '๐ŸŸ'
  
ajaxBurgers('http://burgers.maxpou.fr', (data) => {
  menu.burger = data[0]
})
  
console.log(menu) // {drink: "๐Ÿน", side: "๐ŸŸ"}
  
// ... after ...
console.log(menu) // {drink: "๐Ÿน", side: "๐ŸŸ", burger: "๐Ÿ”"}
      

Callback


$.ajax({
  type: 'GET',
  url: 'http://burgers.maxpou.fr',
  success: function (returnValue) {
    console.log('Success!!')
  },
  error: function (request,error) {
    console.log('Oooopss')
  }
})
    

Callback hell


getData(function (a) {
  getMoreData(a, function (b) {
    getMoreData(b, function (c) {
      getMoreData(c, function (d) {
        getMoreData(d, function (e) {
          // ...
        })
      })
    })
  })
})
    

Promises (ES2015)

promise

Available states:

  • pending: initial state
  • fulfilled: operation completed successfully
  • rejected: operation failed

Structure


var promise = new Promise(function(resolve, reject) {
  // do a thing, possibly async, thenโ€ฆ

  if (/* everything turned out fine */) {
    resolve('OK!')
  } else {
    reject(Error('KO'))
  }
})
      

Example


function ajax (url) {
  return new Promise(function (resolve, reject) {
    const request = new XMLHttpRequest()
    request.open('GET', url)
    request.onload = function () {
      if (request.status === 200) {
        resolve(request.response)
      } else {
        reject(Error(request.statusText))
      }
    }

    request.onerror = function () {
      reject(Error('Network Error'))
    }
    request.send()
  })
}
      

Using & chaining


ajax('http://burgers.maxpou.fr', 'GET')
  .then(burger => eat(burger))
  // equivalent without arrow function
  .then(function () {
    new Promise(function (resolve) {
      return resolve('Save the world: โœ…')
    )}
  })
  // equivalent with arrow function
  .then(() => new Promise(r => r('Go for a ๐Ÿบ now')))
  .catch((e) => {
     console.log(e)
  })
      

Dealing with multiple promises


const eatBurger = new Promise(resolve => resolve('๐Ÿ”'))
const eatTaco = new Promise(resolve => resolve('๐ŸŒฎ'))
const eatHotdog = new Promise(resolve => resolve('๐ŸŒญ'))
      

Promise.all([eatBurger, eatTaco, eatHotdog]).then(values => {
  console.log('You eat: ' + values) // You eat: ๐Ÿ”,๐ŸŒฎ,๐ŸŒญ
})
      

Promise.race([eatBurger, eatTaco, eatHotdog]).then(values => {
  console.log('You were quicker to eat: ' + values) 
  // You were quicker to eat: ๐Ÿ”
})
      

Async / Await (ES2016)


const storage = {
  async getAvatar (name) {
    const cache = await caches.open('avatars')
    return cache.match(`/avatars/${name}.jpg`)
  }
}

storage.getAvatar('maxpou').then(โ€ฆ)
      

Others cool ES2015+ features

Template


const hero = { name: 'Logan', nickname: 'Wolverine', family: 'Marvel' }
      

// es5
var html = "

" + hero.name + "

" html += "" + hero.nickname + "€" html += "" + hero.family + ""

// ES2015
const html = `
  

${hero.name}

${hero.nickname}€ ${hero.family} `

Default parameters


function multiply (a, b = 1) {
  return a * b
}

multiply(5, 2) // 10
multiply(5, 1) // 5
multiply(5)    // 5
      

Import / export


// my-module.js

export default function cube (x) {
  return x * x * x
}
      

// app.js

import cube from 'my-module'
console.log(cube(3)) // 27
      

See also:

The rise of the functional programming

In a nutshell

  • It's a programming paradigm (such as OOP)
  • It's not incompatible with OOP
  • Use a declarative approach (rather than imperative)
  • Brings concepts to produce better softwares

Purity


var number = 2
function incrementBy (increment) {
  launchNuclearMissile()
  return number + increment
}
      
  • This function produce side effects
  • Result depends on the number of execution (not idempotent)

function increment (number, increment) {
  return number + increment
}
      

Immutability


let properties = [{/* */}]
for (let i = 0; i < properties.length; i++) {
  properties[i].price = properties[i].price * 1.2
  properties[i].currency = 'โ‚ฌ'
}
      
  • Break Single Responsibility Principle (S of SOLID)
  • After the loop properties state change. It doesn't represent properties anymore
    Thanks for the headache
  • Non thread-safe code

High order function

A higher-order function is a function that does at least one of the following:
  • takes one or more functions as arguments,
  • returns a function as its result.

function add (x) {
  return function (y) {
    return x + y
  }
}
var add4 = add(4)
add4(38) // 42
add4(4)  // 8
      

Embrace the power of Array


const formatedHighRatedProperties = properties
  .filter(p => p.rate >= 70 || p.isNew)
  .sort((a, b) => b.rate - a.rate)
  .map(p => p.name + ', ' + p.city)
      

Be declarative

Cheat sheet

JavaScript Dev Tools

(That Put Other Dev Tools to Shame)

Browsers Support

Chrome devtools

developer.chrome.com/devtools

NPM

Package Manager (such as Composer)


{
  "name": "js-today",
  "version": "1.0.0",
  "author": "Maxence POUTORD <github@maxpou.fr>",
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "https://github.com/maxpou-slides/js-today"
  },
  "main": "app.js",
  "scripts": {
    "start": "node_modules/http-server/bin/http-server",
    "test": "echo \"Error: no test specified\" && exit 1",
    "lint": "eslint ."
  },
  "devDependencies": {
    "eslint": "^3.12.2",
    "http-server": "^0.9.0"
  }
}
      

Babel

Transform ES2015+ to ES5


// beautiful ES2015+
[1,2,3,4,5]
  .filter(n => n % 2 === 0)
  .map(n => n + 1)

// ugly es5
[1, 2, 3, 4, 5].filter(function (n) {
  return n % 2 === 0;
}).map(function (n) {
  return n + 1;
});
      

Don't forget the babel-preset-env

Webpack (+hot module replacement)

webpack

Framework / Libraries tooling

vue chrome extension

Reading

Reading

Master the JavaScript Interview (E. Elliott)