Block-Level Scoping in ES 2015

Introduction:
Scoping in JavaScript works little different compared to other languages. Without knowing this, many new and experienced developers have fallen prey to Scoping System in JavaScript. This is because of our coding habits in C#, Java or C++.
The Problem:
We usually create variables in for loop or if condition in a function and think that it will not be accessible outside that loop or if block.
Let us see this example, we are creating a variable ‘name’ in if-condition and try to print it in console outside the if-block. One may think that it will output undefined. But to our surprise, the value that is set in if condition will be printed.

function testMethod() {
     if(true) {
           var name = 'Pranav';
           console.log(name); // outputs 'Pranav'
     }
     console.log(name);  // will output 'Pranav'
}

The reason for this behaviour is that unlike other languages JavaScript follows Function-level variable ScopingJavaScript hoists variables to the top of the function no matter where they were declared in the function enforcing a function-level variable scoping.

This behaviour is evil at times and cause potential problems in large scale apps.
To solve this problem, ECMA Script 2015 ( ES 2015) introduces block-level scoping using two new keywords: let and const.
The Solution: Block-level Scoping using let and const
Both let and const create variables that are block-scoped – they only exist within the innermost block that surrounds them i.e., if-blocks, loop-blocks, anything with curly braces and naked curly braces too.
let:
Let us see an example to make it more clear.

function testMethod() {
     let name = 'Ainavolu';
     if(true) {
           let name = 'Pranav';
           console.log(name); // outputs 'Pranav'
     }
     console.log(name);  // outputs 'Ainavolu'
}

Here the scope of variable name in if-condition is limited to that block. It shadows the function-level variable in that context.
Another good thing with let and const is that they have temporal dead zone (TDZ). When entering its scope, it can’t be accessed (get or set) until execution reaches the declaration.

function testMethod() {
     name = 'Ainavolu'; // uninitialized binding - reference error
     console.log(name); // reference error
     let name; // TDZ ends; name is initialized with undefined
     console.log(name);  // outputs 'undefined'
     name = 'Pranav';
     console.log(name); // outputs 'Pranav'
}

const: 
const creates immutable variables i.e., variables whose value cannot be changed.

     const FILE_NAME = 'MyTestLogFile.log';
     FILE_NAME = 'AnotherLogFile.log'; // throws TypeError

Variables created with let are mutable. The value of those variables can be changed any time after declaration unlike const.
Here is an interesting case of const covered by Axel Rauschmayer.

    const obj = {};
    obj.name = 'Pranav';
    console.log(obj.name); // Pranav
    obj = {}; // TypeError
const does not affect whether the value of a constant itself is mutable or not: If a constant refers to an object, it will always refer to that object, but the object itself can still be changed (if it is mutable).
To make an object mutable, we need to use its freeze property.
const obj = Object.freeze();
obj.name = 'Pranav'; // TypeError

Recommended reading: Variables and scoping in ECMAScript 6 by Axel Rauschmayer
 

Promises in ES6

Promises are usually vaguely defined as “a proxy for a value that will eventually become available”. They can be used for both synchronous and asynchronous code flows, although they make asynchronous flows easier to reason about – once you’ve mastered promises, that is.
Consider as an example the upcoming fetch API. This API is a simplification of XMLHttpRequest. It aims to be super simple to use for the most basic use cases: making a GET request against a resource relative to the current page over http(s) – it also provides a comprehensive API that caters to advanced use cases as well, but that’s not our focus for now. In it’s most basic incarnation, you can make a request for GET foo like so.

fetch('foo')

The fetch('foo') statement doesn’t seem all that exciting. It makes a “fire-and-forget” GET request against foo relative to the resource we’re currently on. The fetch method returns a Promise. You can chain a .then callback that will be executed once the foo resource finishes loading.

fetch('foo').then(response => /* do something */)

For example,

fetch('foo', (err, res) => {
  if (err) {
    // handle error
  }
  // handle response
})

The callback wouldn’t be invoked until the foo resource has been fetched, so its execution remains asynchronous and non-blocking. Note that in this model you could only specify a single callback, and that callback would be responsible for all functionality derived from the response.
Few important points about Promises:

  • Follows the Promises/A+ specification, was widely implemented in the wild before ES6 was standarized (e.g bluebird)
  • Promises behave like a tree. Add branches with p.then(handler) and p.catch(handler)
  • Create new p promises with new Promise((resolve, reject) => { /* resolver */ })
    • The resolve(value) callback will fulfill the promise with the provided value
    • The reject(reason) callback will reject p with a reason error
    • You can call those methods asynchronously, blocking deeper branches of the promise tree
  • Each call to p.then and p.catch creates another promise that’s blocked on p being settled
  • Promises start out in pending state and are settled when they’re either fulfilled or rejected
  • Promises can only be settled once, and then they’re settled. Settled promises unblock deeper branches
  • You can tack as many promises as you want onto as many branches as you need
  • Each branch will execute either .then handlers or .catch handlers, never both
  • A .then callback can transform the result of the previous branch by returning a value
  • A .then callback can block on another promise by returning it
  • p.catch(fn).catch(fn) won’t do what you want — unless what you wanted is to catch errors in the error handler
  • Promise.resolve(value) creates a promise that’s fulfilled with the provided value
  • Promise.reject(reason) creates a promise that’s rejected with the provided reason
  • Promise.all(...promises) creates a promise that settles when all ...promises are fulfilled or 1 of them is rejected
  • Promise.race(...promises) creates a promise that settles as soon as 1 of ...promises is settled
  • Use Promisees — the promise visualization playground — to better understand promises

Generators in ES6

Generators are a new feature in ES6. You declare a generator function which returns generator objects g that can then be iterated using any of Array.from(g), [...g], or for value of g loops. Generator functions allow you to declare a special kind of iterator. These iterators can suspend execution while retaining their context.
Here is an example generator function. Note the * after function. That’s not a typo, that’s how you mark a generator function as a generator.

function* generator () {
  yield 'f'
  yield 'o'
  yield 'o'
}

A quick overview of Generators:

  • Generator functions are a special kind of iterator that can be declared using the function* generator () {} syntax
  • Generator functions use yield to emit an element sequence
  • Generator functions can also use yield* to delegate to another generator function — or any iterable object
  • Generator functions return a generator object that’s adheres to both the iterable and iterator protocols
    • Given g = generator(), g adheres to the iterable protocol because g[Symbol.iterator] is a method
    • Given g = generator(), g adheres to the iterator protocol because g.next is a method
    • The iterator for a generator object g is the generator itself: g[Symbol.iterator]() === g
  • Pull values using Array.from(g), [...g], for (let item of g), or just calling g.next()
  • Generator function execution is suspended, remembering the last position, in four different cases
    • A yield expression returning the next value in the sequence
    • A return statement returning the last value in the sequence
    • A throw statement halts execution in the generator entirely
    • Reaching the end of the generator function signals { done: true }
  • Once the g sequence has ended, g.next() simply returns { done: true } and has no effect
  • It’s easy to make asynchronous flows feel synchronous
    • Take user-provided generator function
    • User code is suspended while asynchronous operations take place
    • Call g.next(), unsuspending execution in user code

I highly recommend you go over Axel’s article on generators, as he put together an amazing write-up on use cases for generators just a few months ago.

Iterators in ES6

JavaScript gets two new protocols in ES6, Iterators and Iterables. In plain terms, you can think of protocols as conventions. As long as you follow a determined convention in the language, you get a side-effect. The iterable protocol allows you to define the behavior when JavaScript objects are being iterated.
The code below is an iterable object in ES6.

var foo = {
  [Symbol.iterator]: () => ({
    items: ['p', 'o', 'n', 'y', 'f', 'o', 'o'],
    next: function next () {
      return {
        done: this.items.length === 0,
        value: this.items.shift()
      }
    }
  })
}

Here is a quick snap-in of iterators:

  • Iterator and iterable protocol define how to iterate over any object, not just arrays and array-likes
  • A well-known Symbol is used to assign an iterator to any object
  • var foo = { [Symbol.iterator]: iterable}, or foo[Symbol.iterator] = iterable
  • The iterable is a method that returns an iterator object that has a next method
  • The next method returns objects with two properties, value and done
    • The value property indicates the current value in the sequence being iterated
    • The done property indicates whether there are any more items to iterate
  • Objects that have a [Symbol.iterator] value are iterable, because they subscribe to the iterable protocol
  • Some built-ins like Array, String, or arguments — and NodeList in browsers — are iterable by default in ES6
  • Iterable objects can be looped over with for..of, such as for (let el of document.querySelectorAll('a'))
  • Iterable objects can be synthesized using the spread operator, like [...document.querySelectorAll('a')]
  • You can also use Array.from(document.querySelectorAll('a')) to synthesize an iterable sequence into an array
  • Iterators are lazy, and those that produce an infinite sequence still can lead to valid programs
  • Be careful not to attempt to synthesize an infinite sequence with ... or Array.from as that will cause an infinite loop