ES6 and You: The Future is Now!

JD Hancock (http://photos.jdhancock.com/photo/2013-08-18-224606-do-you-remember-the-future.html)
me

Jordan Kasper | @jakerella

ES6/2015

What's the "ES" and how did we get to 6...
or 2015... or whatever?

What about ES7? Or ES.next?

Topics for Today

  • "Classes" (constructors, extends, super)
  • Default Function Args
  • New Variable Declarations and Scope
  • Template Literals
  • Object Deconstruction
  • Arrow Functions

What about Native Modules?

Native Module support from caniuse.com http://caniuse.com/#feat=es6-module

What about Promises?

They're awesome, and you should look into them...

... especially in combination with the new fetch API
(versus traditional Ajax libraries).

Todo MVC

Our Todo Class


class Todo {

    constructor(text) {
        this.text = text;
        this.isComplete = false;
    }

}
    

let item = new Todo('finish presentation');
    

Our Todo Class


class Todo {

    constructor(text, dueDate) {
        this.text = text;
        this.isComplete = false;
        this._dueDate = dueDate;
    }

}
    

Our Todo Class


class Todo {

    constructor(text, dueDate) {
        this.text = text;
        this.isComplete = false;

        // Default dueDate is tomorrow
        this._dueDate = dueDate || Date.now() + 86400000;
    }

}
    

Default Function Arguments


class Todo {

    constructor(text = 'stuff', dueDate) {
        this.text = text;
        this.isComplete = false;
        this._dueDate = dueDate || Date.now() + 86400000;
    }

}
    

Default Function Arguments


class Todo {

    constructor(text='stuff', dueDate = Date.now() + 86400000) {
        this.text = text;
        this.isComplete = false;
        this._dueDate = dueDate;
    }

}
    

Starting Our App

  • Find our form
  • Listen for submit event
  • Create a Todo item
  • Save the item
  • Render the item

Starting Our App


class App {
    constructor() {
        // kick off all the things...
    }
}

new App();  // Get things started
    

Find the Form


class App {

    constructor() {
        document.querySelector('.create-todo')
            .addEventListener('submit', (eventObj) => {
                eventObj.preventDefault();

                // do the work!
            });
    }

}
    

Some Notes on Arrow Functions

  • No context (this)
  • Implicit return value (sometimes)

No Context


// ES5 callback
someFunction() {
  someNode.addEventListener("click", function (eventObj) {
    console.log(this);  // someNode
  });
}
    

// ES6 arrow function
someFunction() {
  someNode.addEventListener("click", (eventObj) => {
    console.log(this);  // whatever someFunction's `this` is
  });
}
    

Wait, don't I want `this`?

The question is: do you need it?

No Context


[1, 2, 3].map(num => {
    console.log(num);   // 1 (then 2, then 3)
    console.log(this);  // undefined
})
    

someNode.addEventListener("click", (eventObj) => {
    console.log(this);             // undefined
    console.log(eventObj.target);  // someNode (probably)
});
    

One note: if you delegate events this gets tricky!

Implicit Return


[1, 2, 3].map( num => num * num );  //  [1, 4, 9]
    

Note there are no curly braces!

num => num * num

Implicit Return

Only when there is no code block (curly braces).

Back to our Todo Item...

Creating the Item


class App {

    constructor() {
        document.querySelector('.create-todo')
            .addEventListener('submit', (eventObj) => {
                eventObj.preventDefault();

                App.createTodo();

            });
    }

    static createTodo() {
        const text = document.querySelector('.todo-text').value;
        const item = new Todo(text);
    }

}
    

let AND const

a short tangent

let and block scoping


let z = 1;
if (x === y) {
    let z = 10;
    z++;
}
// what is `z` now?
    

`z` will be 1 at the bottom!

let and block scoping


for (var i=0; i<10; i++) {
    // do some stuff...
}
console.log(i);  // i is 10 outside the loop!
    

for (let i=0; i<10; i++) {
    // do some stuff...
}
console.log(i);  // ReferenceError!
    

let vs const


let counter = 1;
counter++;       // totally fine

const index = 1;
index++;         // TypeError
    

let vs const


const o = {};
o.foo = 'bar';  // totally fine
    

constant reference vs. constant value

Back to the main story...

Rendering the Todo Item


class App {
    constructor() { /* ... */ }

    static createTodo() {
        const text = document.querySelector('.todo-text').value;
        const item = new Todo(text);

        // put it into the DOM...
    }
}
    

Template Literals


class Todo {
    constructor() { /* ... */ }

    render() {
        return
            `
  • ${this.text}

  • `; } }

    Template Literals

    
    class Todo {
        constructor() { /* ... */ }
    
        render() {
            return
                `
  • ${this.text}

  • `; } }

    Rendering the Todo Item

    
    class App {
        // ...
    
        static createTodo() {
            const text = document.querySelector('.todo-text').value;
            const item = new Todo(text);
    
            const list = document.querySelector('.todo-list');
    
            list.innerHTML += item.render();
        }
    }
        

    This is fine...

    
    <li class="">
                    <p>finish presentation</p>
                    <time>1508702292483</time>
                </li>
        

    (Yes, that spacing is intentional, but do you care?)

    Getters and Setters

    (ES5, not 6!)

    
    class Todo {
        constructor() { /* ... */ }
        render() { /* ... */ }
    
        get dueDate() {
            return (new Date(this._dueDate)).toLocaleString();
        }
        set dueDate(value) {
            this._dueDate = (new Date(value)).getTime();
        }
    }
        

    User Getters by Property Access

    
    render() {
        return
            `
  • ${this.text}

  • `; }

    Look closesly at the <time> element!

    This is better...

    
    <li data-id="1234567890">
                    <p>finish presentation</p>
                    <time>10/22/2017, 4:07:55 PM</time>
                </li>
        

    Object Destructuring

    I don't like repeating myself...

    
    this.text
    this.dueDate
    this.isComplete
        
    
    render() {
        const { text, dueDate, isComplete } = this;
    
        return
            `
  • ${text}

  • `; }

    What about saving the Todo Item?

    
                              Resource
                               |    |
                             Todo  User
                                    |
                                  Admin

    The Resource Class

    
    class Resource {
    
        constructor() { }  // You need this, but it can be blank
    
        save() { }
    
        get() { }
    
        destroy() { }
    }
        

    Inheritance

    
    class Todo extends Resource {
        constructor( /* params... */ ) {
            super();
    
            // your other code...
        }
    }
        

    Inheritance

    
    function Todo() {
        Resource.prototype.constructor.apply(this, Array.from(arguments));
    
        // ...
    }
    Todo.prototype = Object.create(Resource.prototype);
    Todo.prototype.constructor = Todo;
        

    This is just syntactic sugar!

    ...which is fine,
    but you still need to know about prototypes!

    Back to Our Resource Class

    
    class Resource {
    
        constructor() { }
    
        save() { }  // let's look closer at this...
    
        get() { }
    
        destroy() { }
    }
        

    Validating Data

    
    class Resource {
        constructor() { }
    
        save() {
            // What if there's bad data?
            // We need validation!
    
            // do the save...
            // maybe with fetch()?
        }
    }
        

    Call a "validate" method

    
    class Resource {
        constructor() { }
    
        save() {
            this.validate();
    
            // do the save...
            // maybe with fetch()?
        }
    }
        

    The "vaidate" Method

    
    class Resource {
        constructor() { }
    
        save() {
            /* ... */
        }
    
        validate() {
            if (this.id && typeof(this.id) !== "number") {
                throw new Error("Invalid resource ID");
            }
        }
    }
        

    What about the text, or dueDate?

    We're in the parent class!

    Implement "vaidate" in Todo

    
    class Todo extends Resource {
        constructor() { /* ... */ }
        render() { /* ... */ }
    
        validate() {
            if (typeof(this.text) !== "string") {
                throw new Error("Invalid Todo text");
            }
            // more data checks...
        }
    }
        

    We have a problem... the ID is never checked!

    Implement "vaidate" in Todo

    Don't duplicate, reuse!

    
    class Todo extends Resource {
        constructor() { /* ... */ }
        render() { /* ... */ }
    
        validate() {
            super.validate();
    
            if (typeof(this.text) !== "string") {
                throw new Error("Invalid Todo text");
            }
            // more data checks...
        }
    }
        

    super() versus super.whatever()

    This is still just prototypes!

    (Be careful, it's really just a new keyword that behaves differently in different conditions.)

    More Syntactic Sugar!

    (You still need to know about prototypes.)

    Saving the Item

    
    class App {
        // ...
    
        static createTodo() {
            const text = document.querySelector('.todo-text').value;
            const item = new Todo(text);
    
            try {
                item.save();
                const list = document.querySelector('.todo-list');
                list.innerHTML += item.render();
    
            } catch(err) {
                // Handle the error!
                // (Maybe tell the user something's wrong?)
            }
        }
    }
        

    Well that was fun...

    ... but can I use these today?

    Feature Support

    Feature Modern Browsers? IE 11?
    Classes ◕‿◕ ಠ_ಠ
    Default Fn args ◕‿◕ ಠ_ಠ
    let and const ◕‿◕ o_o
    Template Literals ◕‿◕ ಠ_ಠ
    Object Deconstruction ◕‿◕ ಠ_ಠ
    Promises ◕‿◕ ಠ_ಠ
    Arrow Functions ◕‿◕ ಠ_ಠ
    fetch ◕‿◕ ಠ_ಠ

    Or use a preprocessor!

    BabelJS

    Try it yourself!

    Thank You!

    ES6 and You: The Future is Now!

    Jordan Kasper | @jakerella

    jordankasper.com/es6
    github.com/jakerella/es6-todos