ES6 of the Week

What is ES6 Anyway?

  • ECMAScript 6, also known as ECMAScript 2015, is the (kind of) latest version of the ECMAScript standard. ES6 is a significant update to the language, and the first update to the language since ES5 was standardized in 2009.

ECMAScript? Aren't we learning JavaScript?

  • ECMAScript is a language specification that JavaScript follows (as do other languages like JScript, ActionScript, and more).

A Brief History of Javascript

  • 1995 - Netscape (Brendan Eich) releases LiveScript
  • 1996 - Netscape submits it ECMA International for standardization
  • 1999 - ECMAScript 3 (baseline for modern day JavaScript)
  • 2009 - ECMAScript 5 (ES5)
  • 2015 - ECMAScript 6 (ES6)!

Sparknotes version

  • ECMAScript: The official language name, refers to the standard.

  • JavaScript: Commonly used name for implementations of ECMAScript standard. Not tied to any version.

  • ECMAScript 5 (ES5): 5th edition of ECMAScript, what you're used to writing now.

  • ECMAScript 6 (ES6)/ ECMAScript 2015: 6th edition of ECMAScript, what we're going to be learning.

This Week's Episode:

const and let

arrow functions

Block scope vs. Function scope

  • Remember that, in Javascript,  var declares a  function-scoped variable.
  • Const and let set a tighter scoping - "block" scoping
    • Rather than being hoisted to the top of a function's scope, variables declared with  const and  let are hoisted only to the top of the code block
    • For, if, while, try/catch all create code blocks

Hoisting

function fooWithVar (bar) {
    if (bar) {
        var baz = 'baz'; 
    }
    console.log(baz);
}

// to the JavaScript interpreter:
function fooWithVar (bar) {
  var baz;
  if (bar) {
    baz = bar;
  }
  console.log(baz);
}

fooWithVar('hello world') // hello world
fooWithVar() // undefined

Blocked Out

function fooWithLet (bar) {
    if (bar) {
        let baz = 'baz'; 
    }
    console.log(baz);
}

// to the JavaScript interpreter:
function fooWithLet (bar) {
    if (bar) {
        let baz;
        baz = bar; 
    }
    console.log(baz);
}

fooWithLet(); 
// ReferenceError: baz is not defined


/** What's easier to debug: undefined, 
or a ReferenceError? */

Const

  • Declares a read-only reference to a value - all this means is that variables declared with "const" cannot be reassigned
  • Note that you can still modify the assigned content of the variable (ex. if you assign the variable to an object, you can still modify the object - you just can't assign that variable to something else, like another object)
    • Does not make the variable immutable!
  • You must assign a value to a const-declared variable when you create it. You can't create it first and assign it later.

Const Comment

'use strict';

const foo = [];
const bar = {};
const baz = 1;
const quux = 'hello';


foo.push(bar); // okay
bar.someMethod = function () {} // also okay

baz = 2 // TypeError: Assignment to constant variable
quux += ' world' // TypeError: Assignment to constant variable

"Letting" you in on a cool secret

// remember me?

function createFunctions(n) {
    let functions = [];

    /*  because using "let" to declare the variable "i" makes it block-scoped,
        you don't have to worry about it being closed over    */
    for (let i = 0; i < n; i++) {
        functions.push(function () {
            return i;
        });
    }
    return functions;
}

let myFunctions = createFunctions(5);
myFunctions[0]() // => 0    I work the way you thought I would!
myFunctions[1]() // => 1    Pretty cool, right?

Cleaning up

// the combination of let and arbitrary code blocks can help you free up memory faster

function suchCalculation () {
    /* very function */
}


/* Arbitrary code blocks are also new with ES6
   They prevent scope pollution - no need for a messy IIFE!
*/
{
    let muchData = { /* large dataset */ }
    suchCalculation(muchData);
}

/* At this point, the garbage collector will have cleaned up the large data set we used, 
   since we're outside the code block
*/

console.log('wow!');

Why Use Let and Const?

  • More descriptive way to declare your data
  • Protects your for loops from unwanted closure

Why Continue using Var?

  • Maybe browser compatibility, if you're in an environment where you can't use Babel
  • For the most part...you can replace var with const and let entirely; the Airbnb javascript style guide actually recommends doing just that!

Pt. 2

Arrow functions

"This"

Remember that, within a function, this is defined based on the execution context of a function

  • If a function is executed as a method on an object,  this will refer to the object itself, so that you can intuitively access other properties/methods on the object

What if I told you...

// say we have an object called Neo...
const Neo = {};
// and we also have a function
const bendSpoon = function () {
    console.log(this.spoon);
};
// let's give our object a property and a method
Neo.spoon = 'spoon';
Neo.bendSpoon = bendSpoon;

// the execution context of bendSpoon is Neo, so this refers to Neo
Neo.bendSpoon(); // spoon

// the execution context of bendSpoon is the global context - there is no spoon
bendSpoon(); // undefined

Mr. Anderson...

const Neo = {};
const agentSmiths = ['Smith1', 'Smith2', 'Smith3'];

Neo.kick = function (agent) {
    console.log('Neo kicked ', agent);
};

Neo.kickAgentSmiths = function () {
    agentSmiths.forEach(function (agent) {
        this.kick(agent);
    });
};

Neo.kickAgentSmiths(); // TypeError: this.kick is not a function

// How can we help Neo?

Arrow functions

  • A more concise way to write function expressions
    • Reminder:
      • var fn = function () {}    === function expression
      • function fn () {}              === function declaration
  • The big difference: the body of arrow functions do not get their own dynamic this value. Instead, arrow functions have lexical this: their this context is the enclosing context
    • This solves some issues you may have experienced with callback functions in methods like Array.prototype.forEach

Syntax

/*  In a single-line arrow function, curly braces are optional,
    and the function implicitly returns the value of the last expression.
    You can include a return statement if you'd like, but it's optional.

    This is called the 'concise body'
*/
arr.map(i => i * 2);

//  If you have multiple arguments, they must go in parentheses
arr.reduce((prev, next) => prev + next);

//  If you have no arguments, you can just use an empty ()
let foo = () => console.log('bar');

/* If you need more than one line, then you must use brackets
   and must include the return statement

   This is called the 'block body'
*/
let baz = (x) => {
    if (x > 1) return x;
    else return 1;
}

Using Arrow Functions

const Neo = {};
const agentSmiths = ['Smith1', 'Smith2', 'Smith3'];

Neo.kick = function (agent) {
    console.log('Neo kicked ', agent);
};

Neo.kickAgentSmiths = function () {
    agentSmiths.forEach(agent => this.kick(agent));
};

Neo.kickAgentSmiths();

/*
Neo kicked  Smith1
Neo kicked  Smith2
Neo kicked  Smith3
*/

Arrow Combos

/*
Arrow functions love promise chains and array methods
*/

// Greatest http request of all time
$http.get('/api/artists')
    .then(res => res.data)
    .then(artists => artists.filter(artist => artist.name !== 'Kanye')
    .catch(err => console.error);

Don't overdo it

const Neo = {};
const agentSmiths = ['Smith1', 'Smith2', 'Smith3'];

Neo.kick = function (agent) {
    console.log('Neo kicked ', agent);
};

// now the kickAgentSmith function's this is no longer Neo 
Neo.kickAgentSmiths = () => {
    agentSmiths.forEach(agent => this.kick(agent));
};

Neo.kickAgentSmiths(); // TypeError: the Matrix has you

Other Arrow Function Facts

  • The methods call(), apply(), and bind() will not change the value of this in arrow functions. 
  • There is no arguments object exposed by arrow functions - if you try to use arguments, it will just try to find an 'arguments' variable in the enclosing scope
  • If you want to use concise body to return an object literal, you need to wrap it in parentheses - otherwise, it looks like you're trying to use block body!
  • You can’t use arrow functions with the new keyword, attempting to do results in an error being thrown.

More Pointers

// Call and apply don't affect 'this'
let sum = (a, b) => a + b;
sum.call(null, 1, 2) // 3
sum.apply(null, [1, 2]) // 3

// On that note, don't expect 'arguments' to work
let differenceA = (a, b) => arguments[0] - arguments[1]; // nope
differenceA(2, 1) // TypeError

// See what's happening here?
let returnAnObject = () => { a: 1 };
let obj = returnAnObject();
obj.a; // TypeError: Cannot read property 'a' of undefined

let returnAnObjectForReals = () => ({ a: 1 });
let obj = returnAnObjectForReals();
obj.a; // 1


Why Use Arrow Functions?

  • They look rad and can help turn your shorter functions into clean one-liners
  • Intuitive this context eliminates the need to use self = this in some callbacks

When to be careful

  • Whenever this is involved - make sure you know what kind of behavior you're getting

ES6 of the Week - 1 (const and let; arrow funcs)

By beelai88

ES6 of the Week - 1 (const and let; arrow funcs)

Const & Let / Arrow Functions

  • 621