ES6: ECMAscript Strikes Back

Quest Henkart
6 min readMay 25, 2015

--

For the second edition of my ES6 blog trilogy, we are going to take a quick peek at some really cool features such as, symbols, maps, collections, default parameters, and classes! Again, if you haven’t seen it yet, check out Aaron Frost’s ES6 talk on Pluralsight.

Classes

Classes provide a new syntactical way to create classes and subclasses that is both intuitive and easy to follow. It is important to note that the new class feature offers no new functionality or performance gains (as of yet). Everything you can do with classes you can also do with the current pseudoclassical instantiation pattern and prototype chaining. Classes, however, will make classing and subclassing much easier.

So let’s dive in!

Class Constructor and prototype methods

class Car{ 
constructor(make, model, location){
this.make = make;
this.model = model;
this.location = location;
}

driveTo(destination){
this.location = destination;
}
}

As you can see, the syntax is a lot more explicit. The class handles its methods within its initial instantiation rather than adding individual methods to a prototype after instantiating the constructor. To me, this is a lot more familiar to other languages I’ve worked with before javascript.

Getters and Setters

The new class syntax will also utilize the keyword get and set. A function using the get keyword is written like a function, but treated like a property.

class Car{ 
constructor(...){ ... }
get gasLevel(){
return this.gasLevel;
}
set gasLevel(level){
this.gasLevel = level
}
}
var car = new Car(...)
car.gasLevel // 40%
car.gasLevel = 100%
car.gasLevel // 100%

As you can see, even though the getter and setter functions were written like functions, they were executed like properties. Having a getter function allows an outsider to access properties, including those that may have been hidden or private, and a setter allows outsiders to set or influence those properties in some way. It is an important thing to understand as it allows the developer to explicitly decide what a user can actually touch or change.

This is where the new class system really shines. Subclassing was never difficult in javascript, but the way one went about doing it was a bit obscure. With the new class syntax, creating a subclass is as simple as it gets. Let’s take a look

class Motorcycle extends Car { 
constructor(){
super('BMW', "S1000", "San Francisco")
}
}

Symbols and Private Variables

Symbols offer an exciting way to handle data that you don’t necessarily want to share. It creates a seemingly random set of letters and numbers that represent a key. A symbol is naturally innumerable, meaning it won’t show up when you iterate through keys or check for length, and a symbol cannot be changed in memory.

const vinNumber = Symbol(); 
const licensePlate = Symbol();
let mileage = Symbol();
class Car{
constructor(vin, license, mileage){
this[vinNumber] = vin;
this[licensePlate] = license;
this[mileage] = mileage;
}
}
var car = new Car(...)
console.log(car)
// logs Car{ // __$85109827$23$__ : 2304823432 // __$28934823$12$__ : 23DEH239 // __$59202033$62$__ : 40239 //}

As you can see, there is a heavy dose of obscurity when viewing properties from the outside using symbols. From the perspective of the developer, we will only be dealing with named keys, but if someone looks from the outside, all they see is a strange series of numbers that change every time a new instance is built.

This is really where the getters and setters become important, as you can explicitly provide access to some of these private variables

get mileage(){ 
return this[mileage]
}
console.log(car.mileage) // 40239

Of course, to really make this class constructor secure, we need to wrap the entire thing into a closure so no one can peek at where we are instantiating our symbols and figure out what they mean. To do this, we will throw the entire constructor into an IIFFE — immediately invoked function expression.

(function(){ 
const vinNumber = Symbol();
const licensePlate = Symbol();
let mileage = Symbol();
class Car{ constructor(vin, license, mileage){
this[vinNumber] = vin;
this[licensePlate] = license;
this[mileage] = mileage;
}
get mileage(){
return this[mileage]
}
})()

Default Parameters

How many times have you had to write a function like this?

function example(a){ 
a = a || 3
}

It is a common short circuit evaluation that allows us to provide a default parameter. Now, with ES6, there is an explicit technique for setting default parameters.

function example(a = 3){ 
console.log(a);
}
example("ES6") // ES6
example("") // ""
example() // 3
example(undefined) // 3

As you can see, the default parameter only activates when the parameter is undefined. It won’t activate on a falsey statement.

Default parameters can handle function calls as well, which will only be executed if the variable passed in is undefined

function example(a = getValue(), b, c=3){ }

Default parameters cannot be used on splat arguments. Such a declaration would return an error

function example(a=4, b, c=3, ...rest = 2){} //ERROR

Collections

ES6 is going to offer three new collections that are going to offer significant improvements to the way we do things.

SET

A set is very similar to an Array.
SET has various new methods such as has, add, clear, delete

var set = new Set(); 
set.has(1); // false
set.add(1);
set.has(1); //true
set.clear();
set.has(1) //false
set.add(1)
set.add(2)
set.size; //2
set.delete(2)
set.size; //1

All items in a SET are unique

set.add(1) 
set.size //1
set.add(1)
set.size //1

However, there is no typecasting on uniqueness, meaning elements won’t automatically switch to an integer or string

set.add(1) 
set.has(1) //true
set.has('1') //false
set.add('1')
set.size; //2

Most importantly, Sets have iterators in them which allow us to iterate over the values in a new, very cool way.

var example = new Set(['a', 'b', 'c', 'd']); 
for(let element of example){
console.log(element); //logs 'a', 'b', 'c', 'd'
}

The new of keyword automatically iterates over every single element in a SET.

Map

A Map is very similar to a javascript object. To add values, we use the .set method and check to see if a key/value exists with the .has method. Like SETS, there is no typecasting in Maps, so the integer 1 is an entirely different key than the string ‘1’ which is functionally opposite to the way normal objects work.

var map = new Map(); 
map.set("food", "apples")
map.has("food") //true
map.size //1
map.set(1, "Hello");
map.has('1') //false

Using Objects as Keys
This is where maps really shine. Maps allow us to use javascript objects as keys which will have huge advantages when dealing with serverside data. Let’s dive into an example:

var user = {name: "Quest Henkart", age: 27}; var userContactInfo = new Map(); 
userContactInfo.set(user, ['qhenkart@gmail.com', 'www.questhenkart.com'])

userContactInfo.get(user) // ['qhenkart@gmail.com', 'www.questhenkart.com']

So instead of having to parse through a JSON object to find whatever value we chose to use as a key, we can just pass in their entire user object as the key, and the value it is linked to will be returned.

It is important to note, however, that to access a Map, we must use the same exact key. Having two variables with the exact same data will not offer access to the map, as the key is actually the pointer, not the data itself
for example:

var user1 = {name: "John"} 
var user2 = {name: "John"}
var userTest = new Map();
userTest.set(user1, "test")
userTest.get(user2) //undefined
userTest.get(user1) //"test"

There is one major problem with the Map. If we store something into the Map, say a user, and then delete the user. That user will never be garbage collected because it still exists in the Map. This can lead to major memory problems down the road. Which is why they built our last collection –>

WeakMaps

Weakmaps are almost exactly like Maps. In fact they pretty much work in the same way with one major difference. If a variable is used as a key in Weakmaps, and it is the only place where that variable exists (like using a user as a key and then deleting the user), the weakmap will automatically let it go and let it be trash collected. As a result, a weakmap has

  1. No primitive Keys
  2. No .Size method
"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" --Nicolas Zakas @slicknet

And of course, they are created like this:

var weakmap = new WeakMap(); 
weakmap.set(user, [...])

That’s it for this episode. Tune in next week for the trilogy finale when we dive into Promises and other exciting topics of ES6!

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

--

--