Guest post: What I learned from Aaron Frost's ES6 Course, JS.Next

The following is a guest post written by the incredibly talented Daniel Tsui, on ES6 and the future of JavaScript.

JS.Next: ES6

Aaron Frost ES6Logo

I recently completed an ES6 course, Aaron Frost's JS.Next:ES6, on Frontend Masters.

Every coding video site has a couple of courses that stand out. This course is one of them.
But alas, not everyone has the time to roll through every learning opportunity. I decided to write a summary of the things I learned or found interesting. It is a snapshot of my learning, a condensation of concepts covered over five and a half hours of video with a lot more doubling back and experimenting with new concepts. The slides with code examples are openly available here, and useful for following along.

Off we go!

On Aaron Frost:

Senior Frontend Engineer at DOMO. Talks fast, thinks faster, and appears to genuinely enjoy what he does. He brought a lot of color to the course, and has a great way of elaborating with relevant examples and injecting humor.

Intro/History & ES6 Discussions:

There was this company called Netscape. Brendan Eich, who was working there in the 90s, created JavaScript, then called Mocha, in 10 days. The name ES6 Harmony comes from a bunch of ideas that weren't fully developed when ES3.1 won out over ES4. It took forever, so they called it ES5, and Harmony is the working title of the ES4 ideas being finally realized.

Takeaways:

  • Anyone can follow the development of the language, and of particular note are the discussions going down, often involving a slew of heavy hitters in web development.
  • A great way to stay up to date:
    Follow @esdiscuss, and check out ES6 Discuss.
  • For the more scribely among us, the Mozilla Mailing List is a great resource.

Helpful things to follow along with:

  • Use Traceur REPL to try out the new features. Remember to click 'experimental' in options on the top right before trying to write in ES6. While I develop in Babel, Traceur's REPL seems the most accessible transpiler out there.
  • The compatability table is useful for staying up to date about runtime environments, especially the different ES6 to ES5 compilers. This is actually near the end of the course, but I found it useful to follow along with the table to better understand the features being discussed.

At the time of writing, ES6 just hit Final Draft form.
There's also an intersting discussion developing about immutable data structures, and use of Object.freeze().

Proper Tail Recursion

Proper tail call definition:
* Doesn't need variables in the current call's scope... * Last instruction is a call to another function

Otherwise, control flow would need to return to the current stack, and not be a proper tail call.

Takeaways:

  • Proper tail calls are awesome and result in constant memory usage instead of increasing memory usage.
  • Proper tail calls only work in strict mode right now.
  • Optimize code so that tail position is a tail call...pass it everything it needs

Example of an improper call, with finding fibonnacci numbers:

return fib(n) + fib(n-1)  

CONST, LET, and Block Scoping

Let :
Pre ES6: Javascript is function-scoped, and var always hoists to the top of the function.
Let is Block Scoped.

  • Can't double-declare a variable with let.
  • Can't use let after a var declaration. Only for new variables.
  • Curly braces are a completely new scope.
  • Does Not Hoist, AKA: Temporal Dead Zone: all space before let b has b throwing errors.

Takeaways:

  • Like semicolons, variable hoisting is about forgiveness!
  • It is easier to read/use (for example, in for loops). Of course, supporting legacy browsers is an issue.

Example:

for (let i = 0; i < thing.length ; i++){//... }  


i is accessible inside the block, but nowhere else.

Const :

Takeaways:

  • Can't be assigned again.
  • Lexically scoped inside the curly braces (ie, lexically scoped: only within the block)

Can do block functions...like if(true){}, same as [{} now...
works just like let.
can assign a function to a constant...don't wanna fuck with the prototype...

Rest Parameters:

fn(1, 2, ...rest){  
    console.log(rest);
}

fn(1,2,3,4,5,6) will log [3,4,5,6].

Takeaways:

  • Basically a cleaner re-imagining of the arguments object.
  • One per function
  • Right now, must be last parameter to avoid ambiguity.
  • No default values fn(...[1,2,3])
  • Stop using the arguments object now (or Aaron will punch you in the neck)! In fact, arguments will no longer show up in functions that utilize rest.

The Spread Operator:

Essentially coffeescript splats.
* log(...nums); logs 1,2,3 * You can spread out a certain number of things * Replaces a primary use case for .apply.

Use cases: * new arrays [...arr1, ...arr2] * spread out the results of a method call...

Object & Array Destructuring

I don't think Aaron does the best job of explaining why this is useful. I noticed the live audience also seemed lost at times during this meaty chunk of the course. Admittedly, destructuring is mostly sugary readability goodness, but I'm excited about it because it will make JavaScript accessible to more developers coming from other languages. What really made it click for me was the examples in AirBnb's quiet, benevolent style guide, recently updated for ES6. Being able to clearly pull values out of objects and arrays makes passing return values and using/manipulating what you need a lot easier. I also see it being a step towards making JS easier to write in a more maintainable, functional style.

Takeaways:

  • Can be used to clean up my code, with this pattern:
{city: c, state: s, zip:z} = getAddress();
  • An example of cleaner assignment, with array destructuring:
nums = [1,2,3,4,5,6,7,8,9,10]  
var [one, two,,,,,,,,other] = nums  
log(one, two, other) ; //1,2,10...  
  • And nested arrays...
[one,,[three,,,,[six]]];
  • Essentially, allows you to take things out of an array really quickly/cleanly, instead of wrapping them with individual lines:
var nums = [1, 2, [3, 4, [5, 6]]];

var [one,,[three,,[,six]]] = nums;

one = 1  
three = 3  
six = 6  


* Docs for arrays, with some neat assignment and swapping patterns, such as

[a, b] = [b, a]
  • Could still change, keep your eyes on the compat table above for updates, definitely a use case: Pull apart arrays...

These patterns have immediate benefits when passing input objects to function calls...

function displayPerson({name, age, sex, etc1, etc2})  

The above is more readable & maintainable than the alternative of wrapping a bunch of things in an object to pass. Not to mention pairing this up with...

Default Parameters

Example: This feature automatically defines the input object's name and age properties as specified.
function displayPerson({name = 'bob', age=0})

Takeaways:

  • write less code
  • easier to read
  • improved predictability
  • Like a bunch of other ES6 features, this is just cleaner than standard JavaScript. See the style guide again. Just be careful about side effects.
  • This is the 'Irrefutable Pattern', where we MUST get values on the right side, with the right names. Otherwise, it will throw.
  • The 'Refutable Pattern' is a little different, and only gives the object the specified property if the right side contains a matching name. For example, you could partially refute,
person = {name: "aaron", age: 35};  
let {name, age, ?address} = person;  

or you could could refute the whole patttern:

let ?{x,y,z} = {1,2}  
  • Can be assigned to function calls!
function myFunction(id=getRand()){  
  console.log("My ID: "+id);
}

Functions: Arrows and Implicit Returning

Arrow functions are another CoffeeScript-inspired thing. I think this is a clean, accessible, friendly addition to ECMAScript and use it whenever I can. Knowing where/how to pass context in which enclosing blocks has long been a JavaScript gotcha, but arrow functions abstract most of that away, which will be great for new developers!

Takeaways:

  • Whatever this points to in the enclosing scope. that is what this will point to inside the declared function.
  • All you need to know about the consequences of an arrow function's 'lexical binding', is that you don't need to worry about an arrow function being re-bound, by .call or .apply: it will stay bound to the original this value.
  • Implicit Returning is exactly that, for the ending statements on the right side of the arrow, in function declarations.
var fn2 = () => 2;  

would implicitly return 2.

  • One could leave out the parameters in the above code snippet, but I wouldn't recommend getting into that habit, as it will throw if you have more than one parameter.
var fn2 = (oneThing, twoThing) => 2;  

or

var fn2 = oneThing => 2;  

won't throw but

var fn2 = oneThing, twoThing => 2;  

will.

Classes

From MDN:
JavaScript classes are introduced in ECMAScript 6 and are syntactical sugar over JavaScript's existing prototype-based inheritance. The class syntax is not introducing a new object-oriented inheritance model to JavaScript. JS classes provide a much simpler and clearer syntax to create objects and dealing with inheritance.

Benefits of Classes

  • Easier to learn #noobs
  • Subclassing easier to learn #noobs
  • Code more portable between JS Frameworks
  • Possible performance gains
    ...such as the ones mentioned in 'Misc things', about not needing a multitude of functions with many instances of the same class.

Enter Symbol

Enter the Symbol property, an immutable data type intended to hide make object properties private.

Takeaways:

  • Never generates the same identifier, so what the constructor returns must be saved to a variable to be useful.
  • Don't use with the keyword new.
  • New, unique Symbols be created with a 'key', allowing you to use Symbol.for() to find all the symbols associated with a key. '.keyFor()' does the same thing for Symbol objects, returning the key (if any) that created them
  • As Arron says, "think of it as a generator that never gives the same one twice."

Private Properties

Symbols, especially when created with a family of keys, are typically are used to make properties private, a class member of other Object Oriented Languages, such as C#.
This is intended to make ECMAScript more accessible to developers coming from other backgrounds.

Like so:

const monsterHealth = Symbol();  
const monsterSpeed = Symbol();

  class Monster{
    constructor(name, health, speed){
      this.name = name;
      this[monsterHealth] = health;
      this[monsterSpeed]= speed;
    }
    get speed(){
      return this[monsterSpeed];
    }
    set health(val){
      if(val < 0){
        throw new Error('Health must be positive number');
      }
      this[monsterHealth] = val;
    }
  }

New Iterables: Set, Map, and WeakMap

These collections aren't fully specified and are thus subject to change.

Set

  • Stores unique values.

Map

  • Basic Map, like a Json object.
  • No typecasting in storage.
  • Can use complex objects, functions, and primitives as keys.
  • Custom hash functions (like in Java) are a 'maybe' feature, not currently in ES6.

WeakMap

(From the slides):
Nicolas Zakas:
"A weakmap holds only a weak reference to a key, which means the reference inside of the weakmap doesn't prevent garbage collection of that object."

  • The above is in essence the difference between a Map and a WeakMap. Similar to a Map, but does not keep track of it holds (thus it does not have what have a .size property).
  • Said another way 'if a `WeakMap sees it is the only thing holding reference to a value, that value will be garbage collected'. This is what is meant by a 'weak' reference.

Modules

Import/Export Syntax

MyClass.js

nums = [1,2,3,4,5,6,7,8,9,10]  
var [one, two,,,,,,,,other] = nums  
log(one, two, other) ; //1,2,10...  

Main.js

[one,,[three,,,,[six]]];

Of note, is an example in the slides, demonstrating how you can import multiple things in one line with a new pattern, using an object

var nums = [1, 2, [3, 4, [5, 6]]];

var [one,,[three,,[,six]]] = nums;

one = 1  
three = 3  
six = 6  

Programmatic Loading API

New things:
* System "Module" functions, like .import, .module, '.set', and '.define' * <module> HTML5 Tag * 'Wish I had better explanations'

Promises

Promises are mostly about:
* Making Async code more maintainable & readable * Avoiding the Pyramid of Doom

For more detail, check out the various promise libraries, their performance trade-offs, and other resources. Plug: for my friend Clark, and his lightweight Promise library, iswear.

Takeaways (AKA: New methods to think about):

  • Promise.all(iterable); //Wait Until All Settle
  • Promise.race(iterable);//Wait Until 1 Settles
  • Promise.reject(reason);//Create a promise that is already rejected
  • Promise.resolve(value);//Create a promise that is already resolved

Generators:

This was a tricky abstract concept which took awhile for me to feel like I fully understood.
A generator is basically this function that can pause mid-execution, at yield points, through the .next() method. I recommend better, more complete resources for a comprehensive guide. This is useful for pausing execution & predicting control of Async code. I'll provide a starting point for the beginner, since as Aaron says 'you could literally talk for hours and hours about this stuff'. It goes deep, and he recommends another FE Masters course for a more in-depth exploration of Generators.

  • On *gen functions:
    A generator function, upon invokation, returns a 'generator iterator'

  • On yield():
    "Pause and send this value back"

  • On '.next()': 'Keep going, return the next yield'

    Example 1:

    [a, b] = [b, a]
    

    Example 2:

    function displayPerson({name, age, sex, etc1, etc2})  
    

Miscellaneous Things, Wrap Up:

Aaron mentions the potential long term benefit of classes being the removal of a lot of functions on the objcects. He prefaces that with "I'm not really sure of all this". I found it good for my development to think about these performance concerns.

name The above code will only invoke the passed-in function three times. Logging nums shows a key 4 that points to 5.
If, however, the skipped indexes are filled in, like so:
var nums = [1,2,undefined,undefined,5];,
the function will be invoked five times, and nums won't have an explicity numeric key.

Main Takeaway:

  • A LOT of stuff was not covered in this course, and those things continue to grow, develop, and expand in both number and complexity. A taste:

Last Words:

  • Overall, I was surprised and inspired by how quickly technology in this field develops.
  • Excited to continue learning, both for the pure novelty, and finer tweaks like using new ES6 features to code in a more functional style.
  • I found it helpful to read AirBnB's updated style guide, to get a feel for playing around with all of these new features. Seeing 'bad' examples using the new features is especially helpful, as it points at what is useful about the new feature.

For example:
1. A simple code demo of the [...rest parameter], illustrating why is it superior to the arguments object.
2. In this destructuring example, it's clear that using object destructuring is superior to array destructuring, as it allows us to reason about a function call's return values without considering order.

comments powered by Disqus