ES 6: A New Hope

Quest Henkart
7 min readMay 19, 2015

Wow! I am so excited for ECMAscript 6 I can barely contain myself. Only one month until it is officially released. I just finished watching Aaron Frost’s overview of ECMAscript 6 over at Pluralsight. If you haven’t tuned in yet, check it out here. Over the next three blog posts, I am going to quickly summarize some of the most exciting features of ES6.

Recursion Optimization: Improving Space Complexity

Recursion has always been an elegant and clever way to solve problems. In javascript, however, it can be quite dangerous. Anyone with experience using recursion in javascript is familiar with the call stack exceeded error. This occurs because each stack is stored regardless of whether it is needed or not. In ES6, as long as the stack in question is no longer being referenced, it will be dropped, allowing for a huge improvement in space complexity

Proper Tail Calls (PTC)

The use of a Proper Tail Call is how ES6 will determine whether it can drop a previous stack or not. A PTC occurs when there are no calculations or information that needs to be retained by the function, and all of the information being recursed or passed along is in the tail call, meaning the statement being returned.

return (Anything next to the return statement is the tail position)

Examples of Proper Tail Calls

function foo(n){ 
return 'hello' //this is a tail position but not a tail call

return bar() //this is a tail call, since it is not using anything
//else in the function, the stack can be deleted
}

Examples of Improper Tail Calls

function foo(n){ 
return 1 + bar()
//close, but not a PTC because it must get the
//value of calling bar and then add one and then return
return foo(n) - foo(n)
// not a PTC because the stack must be maintained to fulfill the
// statement
}

Proper Tail Calls will only work in Strict Mode

LET and CONST

ES6 is going to introduce two new variable declarations that are going to completely redefine the way we structure our programs.

CONST

Const is quite simple. When you declare a CONST and set it to some value, it creates a constant that can never be changed.
CONST a = 5
a=1 //returns an error

LET

LET is the most exciting of the new declarations. Using the keyword LET overrides javascript’s native variable hoisting. It removes any ambiguity between development and execution. It also treats any block statement as it’s own scope. Let’s glimpse at a few examples:

console.log(a) // Error if(something){ 
let a = 4
console.log(a) // 4
}
console.log(a) // Error

Notice the differences here between var and let. With var, the first console.log would have returned undefined, the second and third console.logs would have returned 4. This occurs for two reasons:

  1. The use of var would have hoisted the variable declaration to the top of the scope (in this case window), and would have defined it where it was defined in the code

• Let declares and defines exactly where you put it in the code

2. With the use of var, only functions provide new scopes for variable declarations.

  • Let treats each block as a new scope (that includes for loops, if statements, or even ES6's new floating block/Lambda
  • You can now freely reuse i in for loops without worry

Caveats

  • You cannot declare the same variable twice:
var a = 3 
var a = 4
console.log(a) //4
let a = 3
let a = 4 //error
var a = 3
let a = 4 //error
  • While it generally won’t matter to us, there is a Temporal Dead Zone when declaring a variable using let. This means that it actually is secretly hoisted, but it is totally inaccessible, so for 99% of you reading this, you can skip over this bit of information

Conclusion for the use of let

I don’t foresee let completely replacing var. Var still has an important purpose in lexical scoping, but the use of let both adds and removes a major piece of complexity when it comes to variable declarations.

REST/SPLAT/Arguments statement

One of the cool things about javascript is that it is very un-opinionated about how we organize our parameters when instantiating and executing a function. I could have 2 parameters in the declaration, yet pass in 4 parameters in the execution, or visa versa. Javascript handles this by giving us an array-like arguments object for each function. The arguments object contains all of the arguments passed in, including those I provided parameter names for. I can call length on it and I can iterate through it, but it doesn’t have any other properties that arrays have in their prototype chain (such as splice, split, etc). This is solved by converting the arguments object into an array in a somewhat hacky way

 
var args = Array.prototype.slice.call(arguments)
// now I could call any array method on args.

Introducing the REST Parameter
People with experience in Coffeescript or Ruby are already familiar with these sort of parameters. Basically, a rest parameter allows us to separate any extra, undeclared parameters into an array.

var baz = function(a, b, ...rest){ 
console.log(a)
console.log(b)
console.log(rest)
}
baz(5, 6, 10, 11, 12)
//5
//6
//[10,11,12]

The rest array is a fully functional array and can take all methods that are in the array prototype.

Caveats

  • Rest arguments must be the very last parameter
  • the actual name of the rest argument can be named anything, as long as it is preceded with 3 periods

Spread Operators

Spread operators are awesome and simple. It provides us a way to immediately access or assign elements in an array without having to iterate. This can be most easily explained with examples

Accessing elements

var nums = [1,2,3] 
console.log(nums) //[1,2,3]
console.log(...nums) // 1,2,3

combining/concatenating elements

function getNums(){ 
return [1,2,3]
}
var b = [0, ...getNums()]; console.log(b) // [0,1,2,3]

automatic assignment of elements

function returnTwo(a, b) { 
return [b,a]
}
var a =[1,2,3,4,5] var b = returnTwo(a[0], a[1]) //[2,1] – old way var b = returnTwo(...a); // [2,1] – new way

Destructuring

Destructuring represents a concept of automatic assignment of values of objects and arrays into variables

My favorite part of destructuring is the ability to swap variables really easily, without making a temp variable
[b,a] = [a,b]
boom! We just swapped the variables a and b with each other!

More commonly, destructuring is going to look like this

var {city, state, zip} = {city: "San Francisco", state:"CA", zip: 94133} console.log(city) // San Francisco 
console.log(state) // CA
console.log(zip) // 94133

Notice that the variables you are saving them into have to match the keys EXACTLY, or you will get an error. But what if you don’t like the name of the keys? No problem, you can reassign them inline

var {city: c, state: s, zip: z} = {city: "San Francisco",state:"CA", zip: 94133} console.log(c) // San Francisco 
console.log(s) // CA
console.log(z) // 94133
console.log(city) //undefined

So, as you can see, one must know exactly what they are expecting from an object to be able to use destructuring. If you ask for a key that doesn’t exist, you will receive an error. So what if you aren’t 100% sure that the key exists in an object? Well fortunately, they created syntax to support the ‘might exist’ concept:

var {?city, state, ?zip} = {state: 'CA'}

Here the program doesn’t crash, state is assigned and the others are unassigned. It even gets better! If you throw the syntax outside of the braces, it will apply to all of the variables

var ?{city, state, zip} = {nothing: "here"}

Well the variables won’t be assigned, but your program won’t break either. Destructuring is going to be a very handy tool, and will open up many doors for data management.

Arrow Functions

Lastly for this section, we have arrow functions. Probably one of the coolest features I’ve learned about so far. Arrow functions create implicit returns and this binding in javascript. It will look like this:

var fn1 = function(){
return 2
} // old way

var fn2 = () => 2 // new way

It is important to note that brackets are optional for arrow functions, but if you have brackets, there will be no implicit return, so arrow functions are really best for one line functions and anonymous functions. Let’s take a look at what those might look like (it’s about to get really cool)

ES5 syntax:

var nums =[1,2,3]; 
var res = nums.map(function(n){return n*n})
console.log(res) // [1, 4, 9]

Arrow function — ES6 syntax:

var nums = [1,2,3] 
var res = nums.map(n => n*n);
console.log(res); //[1,4,9]

How cool is that!!!!! The syntax for anonymous functions and callbacks have always seemed incredibly bulky and superfluous to me. Now we have quick, easy, and elegant syntax.

Speaking of anonymous functions or callbacks, any practitioner of javascript has struggled with asynchronous calls and losing the this keybinding.

For example, let’s assume I have a pseudoclassical car:

Car.prototype.drive = function(){ 
this.move()
setTimeout(this.turnLeft, function(){
console.log(this) //will log Window
})
}

We should all be familiar with the solutions to this problem. Either we use .bind, to bind the this binding to the turnLeft function, or we set a var context = this. Arrow functions have an awesome automatic this binding that makes the .bind method or setting an outer context unnecessary. It looks like this:

Car.prototype.drive = function(){ 
this.move()
setTimeout(this.turnLeft, () => console.log(this)) //will log Car }

That will surely save many headaches!

I hope you guys have enjoyed the first installment of my ES6 overview.

Originally published at questhenkart.com on May 19, 2015.

--

--