Day 3: Modules, Classes and More

Once upon a time, JavaScript had no modules and it was bad, and no one lived happily ever after, and there was agony had by the princes. Then some very wise developers went Into The Woods and retrieved the magical gemstone and they realized that JS had modules all along. It was there the whole time, they just needed a standard for it. The developers created CommonJS and AMD and UMD and some other ones no one remembers because nodejs picked one and it was the chosen one.

Things were good in the land for awhile until the people started to realize that standards should really be built in. People were dynamically exporting modules and these were not statically analyzable, and the children from earth worried that module bundling would never work unless modules could only be static. So all the magical creatures and the children of earth came together and invented a proper module format that changed everything in the land forever and the evil witch was finally defeated.

The ES6 module syntax is simple. It has two keywords export and default. You can export multiple things using export like this:

// module magicalWorld.js
export {
  magic,
  wellspring,
  portals,
}

Then import them like this:

import { magic } from 'magicalWorld'

If you want to export a default thing you can do it like this:

// module Arendelle.js
export default Ice

And then import it like this:

import Ice from 'Arendelle'

If you want to, you can rename things you import like this:

import { portals as wardrobes } from 'magicalWorld'

ES6 Classes

There was once a time when we had to create classes manually with libraries, those dark times have long since past. These days we can create classes using a new amazing syntax:

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

  get awesomeLevel() {
    return this.level + 1
  }

  set awesomeLevel(level) {
    this.level = level
  }

  talk() {
    return `Meow my name is ${this.name}`
  }
}

const cat = new Cat('Slightly Irritated Cat')
cat.awesomeLevel= 9000
console.log(cat.awesomeLevel)
cat.talk()
Try in REPL

Internally classes in ES6 compile to functions. The functions aren't all that different than older techniques for creating classes. The above compiles to something that looks a bit like:

function _createClass(klass, methods) {
  // lots of stuff to turn the methods into properties on a object
  const cat = {
    talk() {
    }
  }

  return cat
}

function Cat(name) {
  this.name = name;

  return  _createClass(Cat, [{
    key: 'talk',
    value: function talk() {
      return 'Meow my name is ' + this.name;
    }
  }, {
    key: 'awesomeLevel',
    get: function get() {
      return this.level + 1;
    },
    set: function set(level) {
      this.level = level;
    }
  }]);
}

Classes are really kind of simple and just syntatic sugar over objects and prototypal inheritance.

Getters and Setters

You can add a getter and setter for a prop with the get and set keywords:

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

  get awesomeLevel() {
    return this.level + 1
  }

  set awesomeLevel(level) {
    this.level = level
  }
}

Warning

It is important you don't set or get a property of the same name in a getter or setter. The following would lead to a stack overflow:

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

  set name(name) {
    this.name = name
  }
}

Static Methods

You can add a method to the class itself by using the static keyword. It looks like this:

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

  static areCatsAwesome() {
    return 'Yes they are!'
  }
}
Cat.areCatsAwesome()
Try in REPL

That does it! Now you are Neo. You know classes.

ES6 Template Literals

Imagine you just woke up. You are in a room. You don't know this room. Your head hurts. The room smells of sulfur or something that you think sulfur would smell like because you have never smelled sulfur but assume it smells like burnt top ramen. A light hangs from the ceiling like it got kicked by a ninja and the villains never got around to repairing it.

Men in dark suits burst into the room. They drag you down a dark hallway where clearly a telekinetic powered human must have unleashed her powers and destroyed the hallway. Do they ever repair things here? They shove you into another dark room with a terminal. You sit down. Your task:

  Construct a string containing variables without using =+=. This string should be wrapped in a function that outputs an evil plan

ES6 introduces template strings which allow you to put variables in a string. To use them you simply use special quotes, the angled ones "`" and then place your variables in ${}.

You sit down and craft the following function:

const printEvilPlan = (evilPlan) => {
  return `
Our evil plan is:
    ${evilPlan.description}
The number of people we will profit from:
    ${evilPlan.profit}
  `
}
printEvilPlan({ description: 'Disrupt', profit: 9001 })
Try in REPL

The lights suddenly go out in the room and the terminal flickers and shuts off. The men rush into the room and pull you out. You are shoved back into the room that smells of burnt top ramen.

Tagged template literals

Days later, the men rush in again and pull you out and shove you into the dimly light room with the terminal again.

Construct a function that highlights a string of evil plans with Markdown.

ES6 introduces template tags which allows us to pass in a string to a function and process both the string and their values. For example, if we had some html templates we could use a library with an html template tag to process these templates:

import html from 'yo-yo'

function bearsDontDigOnDancing(bears) {
  return html`<ul>
    ${bear.map((bear) => {
      return html`<li>${bear}</li>`
    })}
  </ul>`
}

const el = bearsDontDigOnDancing([
  'grizzly',
  'polar',
  'brown'
])

document.body.appendChild(el)

A template tag takes strings, the variables, and returns a new string. The signature looks like this:

const templateTag = (strings, ...values) = {
  return 'new string'
}

Before we solve our assignment we will construct the string we want to parse:

const evilPlan = {
  description: 'Disrupt',
  profit: 'a lot',
}

const evilPlanTemplate = highlightEvilPlan`
My evil plans is to: ${evilPlan.description}
The profit expected is: ${evilPlan.profit}
`

Now let's construct our template tag:

const highlightEvilPlan = (strings, ...values) => {
  const result = ''
  strings.forEach((string, i) => {
    result += string + '*' values[i] + '*'
  })
  return str
}

Our completed thing then looks like this:

const highlightEvilPlan = (strings, ...values) => {
  let result = ''
  strings.forEach((string, i) => {
    result += string + '*' + values[i] + '*'
  })
  return result
}

const evilPlan = {
  description: 'Disrupt',
  profit: 'a lot',
}

const evilPlanTemplate = highlightEvilPlan`
My evil plans is to: ${evilPlan.description}
The profit expected is: ${evilPlan.profit}
`

evilPlanTemplate
Try in REPL

The lights illuminate above your head. There is a window in front of you and you see the men in the dark suits. The men come into the room.

Welcome to Uber! You are hired!