Skip to content

Instantly share code, notes, and snippets.

@njj
Last active August 29, 2015 14:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save njj/74046fea9a4c61477db9 to your computer and use it in GitHub Desktop.
Save njj/74046fea9a4c61477db9 to your computer and use it in GitHub Desktop.
justicen-ES6

ES6

It's about time I write a post about ES6 and the features I'm most excited about. I was an early adopter of ES6 in most of my personal projects and have been promoting it heavily at the office for quite some time. Being able to watch the progress of TC39 has been really exiting, and I feel fortunate that they have opened up the es-discuss mailing list to allow people like myself to get a peek at what's going on. The newest version of JavaScript is being called "the best JavaScript we've ever had". The features we are being given are substantial, and greatly change the future of how JavaScript will be written. Thus far, from my perspective, ES6 has been mostly adopted with open arms. While a few features are being critiqued for various reasons, I'd like to use this post to highlight what I find good rather than focusing on potential bad parts (maybe another post?).

I'm going to highlight a few of the new features that I have been using thanks to awesome transpilers like Babel and bleeding edge releases of browsers. For those who haven't tried any of the new features of ES6, I highly recommend the Babel REPL. It is an awesome way to quickly test out ideas and try these new features, without having to worry about running things locally.

Promises


While promises are nothing new to JavaScript, and have been provided by various libraries for a while, having them standardized as part of the language is awesome. A promise is defined as an object that is used for deferred and asynchronous computations.

To best describe how they work, lets look at some code. The following is an example of how you would utilize ES6 Promises to handle an AJAX request.

function fakeAjax(url) {
  return new Promise(function(resolve, reject) {
      // setTimeouts are for effect, typically we would handle XHR
      if (!url) {
        return setTimeout(reject, 1000);
      }
      return setTimeout(resolve, 1000);
  })
}

// no url, promise rejected
fakeAjax()
  .then(function() {
    console.log('success');
  })
  .catch(function() {
    console.log('fail');
  });

// url, promise resolved
fakeAjax('google.com')
  .then(function() {
    console.log('success');
  })
  .catch(function() {
    console.log('fail');
  });

While this example is trivial, the idea is to simulate an AJAX call. The example can be ran here.

Block Scope


Traditionally speaking, JavaScript has "function based" scope. In short, this means that in order to contain a private variable one must do so by declaring a function. Unlike other languages that have "block based" scope, where any block (i.e. if statement) will create new scope and contain the variable.

For a lot of people coming from other languages, this is a bit of a headache. While a lot of JavaScript developers, including myself, sort of enjoy the quirkiness that comes with JavaScript's scoping - the let keyword enables us to write to utilize "block based" scoping. For those unfamiliar with how JavaScript's scoping works, I recommend doing some research on hoisting.

Here is a trivial example:

function foo() {
  let bar = true;
  
  if (bar) {
    var baz = 'hi!';
  }
  
  console.log(baz); // hi
}

foo();

function foo2() {
  let bar = true;
  
  if (bar) {
    let baz = 'hi';
  }
  
  console.log(baz); // Uncaught ReferenceError: baz is not defined
}

foo2();

As you can see in this example, the first function consoles out 'hi', while the second one throws an error. Try it out here.

Arrow Functions


Arrow functions are probably one of the features I am most excited about. The arrow or =>, is shorthand for the function keyword but with a little added bonus. That amazing bonus is that arrow functions share the same this with their surrounding code. Seasoned JavaScript developers are famailiar with patterns such as var self = this; or var that = this, in order to correctly reference an outer this. But thanks to the =>, that pattern is no longer needed.

Here's an example of our previous lives and our new shiny lives:

// without arrow
var adder = {
  num: 2,
  nums: [1,2,3,4,5],
  addIt: function() {
    var self = this; // note the reference to the outer `this`
    return this.nums.map(function(n) {
      return self.num + n;
    })
  }
};

console.log(adder.addIt()); // [3, 4, 5, 6, 7]

// using arrow
var adder = {
  num: 2,
  nums: [1,2,3,4,5],
  addIt() {
    return this.nums.map(n => {
      return this.num + n;
    })
  }
};

console.log(adder.addIt()); // [3, 4, 5, 6, 7]

As you can see in the second example, there is no need to reference the outer this and this refers to the object in which the addIt function belongs to. The example for that can be found here.

Generators


Generators are a new (to JavaScript) type of function that enables us to suspend (yield) our function for any given amount of time that we want and return several different values throughout the series of executions. I previously wrote a rather extensive blog post on this feature, as it is probably the feature I most excited about.

Here is a quick example of a function that is suspended, until its inner variable count has reached a certain condition.

function * countToFive(){
  var count = 0;
  
  while (count <= 5)
    yield count++;
}

var gen = countToFive();

console.log(gen.next()); // value: 0, done: false
console.log(gen.next()); // value: 1, done: false
console.log(gen.next()); // value: 2, done: false
console.log(gen.next()); // value: 3, done: false
console.log(gen.next()); // value: 4, done: false
console.log(gen.next()); // value: 5, done: false
console.log(gen.next()); // value: undefined, done: true

As you can see, the output of the generator will give you the status. Wherein the value is updated each time the function is called and the done property will update from false to true once we have reached the end of the function. This is done by using the yield keyword, which will pause the function. Mozilla best describe the keyword as "generator-based version of the return keyword".

Here is the Babel link for the example above.

If you would like to know more detail about the generator function and how it works, I recommend checking out MDN, Kyle Simpon's post (on David Walsh's blog), or my own post which again can be found here.

Import (Modules)


With the help of libraries like Require.js and Browserify we have been using module loading in our projects. The definitions of these libraries are set by their authors, and TC-39 has taken into the consideration the popularity of these various libraries. With ES6 we now have module definitionwith in the language itself that attempts brings both patterns together (AMD and CommonJS).

The syntax for module definition is as follows:

// myModule.js
export function myModule(someArg) {
  return someArg;
}

// main.js
import {myModule} from 'myModule';

myModule('foo'); // 'foo'

In addition to this basic syntax, you can export several things in one module and import the entire module as a particular namespace. Then you can call each method with that module by its new namespace.

// myModule.js
export var foo = 'foo';
export var bar = 'bar';

// main.js
import * as baz from 'myModule';

baz.foo; // 'foo'
baz.bar // 'bar'

Classes


Classes are a bit controversial. The main reason for this is that behind the scenes classes are not implemented as classical inheritance in the same way they would be in another languages. Traditionally, JavaScript has prototypal inheritance and if you would like to know more about how that works I recommend checking out this or this article. So what this means is that the new class keyword is mostly syntactic sugar on top of our traditional prototypal inheritance.

Let's see how that works:

class Player {
  constructor(x, y) {
    this.x = 0;
    this.y = 0;
  }
  
  getPosition() {
    return this.x + ',' + this.y;
  }
  
  jumpFoward() {
    this.x = this.x + 1;
  }
  
  jumpBackwards() {
    this.x = this.x - 1;
  }
}

class Archer extends Player {
  constructor(name, x, y) {
    super(x, y);
    this.name = name;
    this.weapon = 'bow';
  }
}

let archer = new Archer('Legolas');

console.log(archer.getPosition()); // '0,0'

// move player forward 1
archer.jumpFoward();

console.log(archer.getPosition()); // '1,0'
console.log(archer.weapon) // 'bow'

You can play with this example here.

Optimized Tail Calls


JavaScript is expressive and powerful in that functions are considered first class. In short, this means we can pass around functions freely. What this enables us to do is perform functional programming techniques. One such technique is recursion. Recursion is essentially the act of a function calling itself, which can be useful for iteration.

As of ES5, recursively calling a function to excess will cause a stack overflow (reach of memory limit). The reason this happens is due to the fact that every time the function calls itself, it is calling a new version (which is subsequently added to the stack) but optimized tail calls utilize the same context of the previously called recursive function. This optimization allows memory usage to remain stable.

In most cases, this is where an example of the Fibonacci sequence or of a factorial function would be provided using recursion. I'll spare the Internet of another trivial example and link other's examples here and here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment