Five features that will make your Javascript code better right now. Use just these good parts of the New Javascript, and wait on the rest.

Needless to say, we care a lot about Javascript performance and features, which is why ES6 is so exciting to us.

The changes in ES6 have been massive, exciting, and confusing. In this essay I’ll introduce five features of the new JavaScript that you can use right away to supercharge your code, and what features to hold off on.

Ecma-what?

JavaScript has had many versions over the years, but overall the evolution was slow until recently. I think More changes have happened in the past two years than in the previous twenty. ES 6 stands for EcmaScript 6. It’s also called EcmaScript 2015, or ES 2015. These are all different names for the ‘new Javascript’.

I’m not entirely sure why JS has changed so much recently, but it seems to be because the owners of the major JavaScript engines have finally become interested in pushing the language forward.

Also, the advent of transpilers like TypeScript have made it possible to use new language features before they are built into the browsers. These two have combined to push JavaScript forward massively.

JS matters because it’s the fabric of the web, and increasingly used in server side apps with Node, and in mobile and desktop apps with Cordova, React Native, and Electron.

In short: JS is everywhere

So it matters that we push it forward. Languages which don’t evolve start to die. Improving the language means we can improve our code. Our apps can have fewer bugs. And some of these features enable the runtimes to actually run code faster. So let’s dive in, starting with variables.

The Problem with Variables

In Javascript when you want to create a variable you use the var keyword. var is great, but it has a couple of problems. First, variables always vary. There is no way to make a true constant in Javascript. You can sorta fake it by creating an object with an immutable property. You can override the setter of the property so that it can’t be set by outside code, but it’s a lot of extra typing and requires an entire object instead of a simple variable.

var VARS = {
    foo
        set = function() { }
        get = function() { return 42 }
}
VARS.foo = 42; // now I have a constant

Regular Javascript variables also have a scoping problem. Take a look at this code.

function badCode() {
    if(foo) {
        var x = 5;
    }
    if(bar) {
        console.log(x);
    }
}

Variables declared with var are global to the function they are defined in, not the block. You’d think that the log statement above wouldn’t work because bar is defined in a different block. and in a language like java or C# you’d be right, but in Javascript var is global to a function, not a block.

It gets worse when we add in variable hoisting. Consider this code

function badCode() {
    console.log("foo is",foo);
    var foo = 'bar';
}

I’m using foo before I’ve defined it. What does this code even mean? Under the hood the javascript engine will hoist the variable declaration to the top of the function block. I can sort of see why you might want to hoist functions, but variable hoisting makes it very easy to introduce subtle bugs that are a pain to diagnose.

Look at this for loop:

for(var i=0; i<5; i++) {
    console.log(i);
}
console.log(i);

The variable is only used inside the loop, but I can still reference it outside. This is just tons of bugs waiting to happen.

The good news is we don’t have to use var anymore. Instead we can use const and let.

Introducing const and let

The new keyword const does exactly what the name suggests. It makes a real constant. If you try to set the constant you’ll get a runtime error. Even better, code linting systems can detect this kind of bug at compile time now, so you can find bugs earlier at development time rather than in production.

The other new kind of variable is created with the let keyword. let is just like var but it is scoped by block instead of function. Now it does what we expect.

function goodCode() {
    if(foo) {
        let x = 5;
    }
    if(bar) {
        console.log(x); //error
    }
}

Let’s revisit the for loop example:

function goodCode() {
    for(let i=0; i<5; i++) {
        console.log(i);
    }
    console.log(i); //error
}

Now the i variable is restricted to the body of the for loop. There is no way it can be used outside accidentally. Also, let isn’t hoisted, so all of those magic moving variables go away.

The new keywords const and let are a complete replacement for var. With modern browsers and the newest versions of Node, there is no reason to use var anymore. Good riddance.

Super Strings

ES6 introduces a new type of string called Template Literals. I prefer to call them Super Strings. You use a super string just like a regular string, but instead of using single or double quotes, you use the back quote.

var q  = 'foo';
var qq = "foo";

var bq = `foo`;

var qq = "Sally sells \"sea shells\"";
var bq = `Sally sells "sea shells"`;

So far so good, but nothing is very different. It does have one immediate advantage. If you need to use double or single quotes inside of a string you don’t have to escape them anymore. However, super strings have a few other tricks up their sleeves.

MULTILINE STRINGS

Finally we can have real multi-line strings. Need to quote several lines of something? You don’t have to escape newlines or do join tricks anymore. Just put in the newlines directly and it works.

var qq = "this is a very long"
  + " piece of text that goes over several lines"
  + " and would require silly hacks";

var bq = `this is a very long
    piece of text that goes over several lines
    and would require silly hacks`;

EXPRESSION ESCAPING

Another new feature is expression escaping. Within a super string you can put ${} with any valid Javascript expression inside the brackets. This is much cleaner than double escaping your quotes, and recent IDEs will syntax highlight these expressions nicely.

var name = "Alice";
var greeting = `Good morning ${name}`;
var amount = 5;
var price = 3;
var sales = `That costs ${amount*price} dollars`;</pre>

Combining expression escaping with multiline support gives us great HTML templates.

var template = `
  

Good Morning ${name}

That item will cost you ${amount*price} dollars.
`

Arrow Functions

So that was strings and variables, but now let’s take a look at functions. If you’ve heard anything about ES6 before, it was probably about arrow functions. These are a different syntax to write regular functions but with a more compact syntax. They also have one very important difference: the this variable means something different. Let’s look at some code.

Suppose you want to loop over an array to double the values within it, producing a new array. You could do it with the following for loop, but that creates extra variables and it can be easy to accidentially break early or get the index wrong. plus it’s a lot of extra typing.

var output = [];
for(var i=0; i<;input.length; i++) {
    output[i] = input[i] * 2;
}

Javascript arrays have a method called map which calls a function on every element to generate a new element, which are then placed into a new array. It works like this:

var output = input.map(function(x) {
    return x * 2;
});

This looks better but it would be nice to make it smaller. The x*2 part is the only thing which actually does any work. The rest is syntactic overhead. With an arrow function we can do it like this:

var output = input.map((x)=>x*2);

Woah! That’s a lot smaller. Let me explain what happened. An arrow function lets you rewrite a function without the actual function word. Instead you put the => after the parenthesis containing the function parameters.

//regular
function (x) {
    return x * 2;
}
//arrow style
(x) => {
    return x * 2;
}

Arrow functions let us write the same thing smaller. But we can make it event shorter. Let’s remove the whitespace. Same code just shorter.

(x) => { return x * 2; }

But we can make it even shorter still. If the arrow function contains only a single expression, we can remove the return and the braces and semicolon, resulting in a tiny one line expression which automatically returns it’s value. This is so much cleaner.

(x) => x * 2
var output = input.map((x)=>x*2);

Arrow functions can make your code very compact and powerful. But there’s one more trick up it’s sleeve. It fixes this.

THE CURSE OF THIS

In Javascript the magic variable this always refers to the object that the function is called on. So code like the following doesn’t do what you think it does.

function App() {
    this.clicked = false;
    var button = document.getElementById('button');
    button.addEventListener('click', function(evt) {
        this.clicked = true; //won't do what you think
    });
}

When you are using other objects the this context may be different than what you expect. When you pass a function to somewhere else to be called back, it may call the function with a different ‘this’. If you add an event handler to a button the button will be the ‘this’. Sometimes that’s what you want, but in the code above it isn’t. We want this to be the surrounding App object, not the button.

This is a long standing problem with Javascript. It’s so common that developers have created something known as the self pattern where you save the correct this reference using a temporary variable self. It’s yucky but it works.

function App() {
    this.clicked = false;
    var button = document.getElementById('button');
    <b>var self = this;</b>
    button.addEventListener('click', function(evt) {
        self.clicked = true;
    });
}

Another way to solve the problem is to bind the function. The bindmethod forces the this to be a specific object, regardless of how the function is latter called.

function App() {
    this.clicked = false;
    var button = document.getElementById('button');
    var callback = (function(evt) {
        this.clicked = true
    })<b>.bind(this);</b>
    button.addEventListener('click',callback);
}

Again, this will work but it’s not great. We now have extra code to write, and underneath doing a bind can create extra overhead. Arrow functions give us a better way to do it.

function App() {
    this.clicked = false;
    var button = document.getElementById('button');
    button.addEventListener('click',()=>{
        this.clicked = true;
    });
}

Arrow functions automatically capture the this var from the surrounding context of where the function is defined, not from where the function is used. This means you can pass the function to some other place and absolutely know that right version of this will be used. In the code above everything works perfectly without any yucky hacks.

In short, arrow functions are really awesome. I try to use them everywhere I can. They make your code shorter, easier to read, and this becomes sensible again.

Promises

Another great feature of arrow functions is that they work well with Promises. A Promise is a new kind of object in Javascript designed to help with things that take a long time to execute. Javascript doesn’t have threads so if you want to do something that might take a long time then you have to use callbacks.

For example, in Node you might want to load a file, parse it, make a database request, then write a new file. These must all be done in order, but they are all asynchronous so you have to start nesting your callbacks. This produces what JS hackers like to call The Pyramid of Doom. You get massively nested code.

fs.readFile("file.txt", function(err, file) {
    db.fetchNames(function(err, names) {
        processFile(file, names, function(err, outfile) {
            fs.writeFile('file.txt',outfile, function(err, status) {
                console.log("we are done writing the file");
            })
        });
    })
});

This code is ugly, hard to reason about, and has lots of nasty corners for bugs to hide. Promises help us defeat the pyramid of doom.

A Javascript Promise is an object that represents a value which may not be available yet. It promises to have the value in the future. You can add a callback to be invoked when the final value is ready using the thenfunction.

var prom = makeSomePromise();
//value not ready yet
prom.then((value)=>{
    //do something with the value
})

Promises with then callbacks are much like traditional callbacks, but promises add an extra twist: they can be chained. Let’s revisit our code from before. Each of the functions must be called in order, and each depends on the result of the previous one. Using promises we can do it like this instead.

    fs.readFile("file.txt")
        .then((file) => {
            return db.fetchNames().then((names)=>{
                return processFile(file,names)
            })
        })
        .then((outfile)=>{
            return fs.writeFile('file.txt',outfile);
        })
        .then(()=>{
            console.log("we are done writing the file");
        });

Notice how the arrow functions make this nice and clean. Each then()callback returns a value. This value is passed to the next one so all of our functions can be easily chained.

Now you’ll notice that the processFile command needs the result of the previous two values however a promise only passes one value. We also don’t care what order readFile and fetchNames happens in. We just want to know when both are completed. Promise can do this too.

PROMISE.ALL

Suppose want to load each file from an array of filenames and be be notified when they are all done. We can do that with Promise.all(). all is a utility method on Promise which takes an array of promises and returns a new promise that resolves when all of the sub-promises complete. Here’s how we would load all of the files with Promise.all. (assume that readFile is a function which returns a promise to read the file).

var proms = filenames.map((name)=> readFile(name));
Promise.all(proms).then((files)=>{
    console.log(`we have ${files.length} files`);
});

Now we can rewrite our original Node example like this:

Promise.all([
    fs.readFile("file.txt"),
    db.fetchNames()
])
.then((vals) => processFile(vals[0],vals[1]))
.then((outfile)=> fs.writeFile('file.txt',outfile))
.then(()=> console.log("we are done writing the file"));

I’ve combined the read file and database calls into a single promise using Promise.all. The value it will return is an array containing the results of both sub-promises, so I can then put them into processFile. Notice that I’ve also used the abbreviated arrow syntax to make the code smaller and cleaner.

HANDLING FAILURE

Now consider what happens if one of these Promises fails? To handle the first one failing we could put in a try / catch block, but the next then will still be called. Really we want everything to stop if the first one fails. Promises have another trick up their sleeves: the catch callback.

In the new version below if anything fails it will immediately jump to the catch callback at the end, skipping the rest. After the catch we can still add more then clauses.

Promise.all([
    fs.readFile("file.txt"),
    db.fetchNames()
])
.then((vals) => processFile(vals[0],vals[1]))
.then((outfile)=> fs.writeFile('file.txt',outfile))
.then(()=> console.log("we are done writing the file"))
.catch((e) => {
    console.log("some error happened");
});

MAKING YOUR OWN PROMISES

Of course promises only work if the APIs we call actually use them. Lots of libraries are starting to switch over to promises, but while we wait we can make our own promises too. We do it with the Promise constructor.

function makePromise(foo,bar) {
    return new Promise((resolve, reject) => {
        try {
            //do long stuff
            resolve(value);
        } catch {
            reject(error);
        }
    });
}

It takes two values: resolve and reject. Each of these are callback functions. Inside you do whatever you need to do that takes a long time, even if it involves multiple callbacks. When you are completely done invoke resolve() with the final value. This will then be sent the to the first then clause of whatever uses your promise.

If something bad happens and you want to reject the value, instead of throwing an error use the reject(), passing whatever alternate value you want.

Here’s a real-life example. I use AJAX calls all the time but they can be ugly, like this.

var url = "http://api.silly.io/api/list/e3da7b3b-976d-4de1-a743-e0124ce973b8?format=json";

var xml = new XMLHttpRequest();
xml.addEventListener('load', function() {
    var result = JSON.parse(this.responseText);
    console.log(result);
});
xml.addEventListener('error',function(error) {
    console.log("something bad happened");
});
xml.open("GET",url);
xml.send();

Let’s wrap this code up in a promise.

function doGet(url) {
    return new Promise((resolve,rej)=>{
        var xml = new XMLHttpRequest();
        xml.addEventListener('load', function() {
            var result = JSON.parse(this.responseText);
            resolve(result);
        });
        xml.addEventListener('error',function(error) {
            reject(error);
        });
        xml.open("GET",url);
        xml.send();
    });
}

This is still the same basic code, but I can call it like this instead.

var url = "someapi.com/dostuff?foo=bar";
doGet(url).then((obj)=>{
    console.log(obj);
});

Wow, that’s so much cleaner. In reality I don’t need make my own AJAX Promise wrapper because there is a new web standard called Fetch which already does this for me. Fetch isn’t quite supported in all browsers yet, so we can use our own until then.

Promises can be a bit tricky to wrap your head around at first, but once you start using them I think you’ll really like them. They make it very easy to pull multiple functions together into a single workflow that makes logical sense, and to properly catch all errors along the way.

Arrays

Finally I want to show you a bunch of new Array features. Most of these aren’t exactly new for ES6, and in fact some of them are quite old. However, they are finally supported everywhere and play nicelly with Arrow Functions and Promises, so I think of them as new.

Suppose you want to do something to every element of an array. Instead of using a for loop, use the forEach or map functions.

var values = [1,2,3,4,5,6,7,8,9,10];
values.forEach((x)=>console.log(x));
var doubled = values.map((x) => x*2);

The forEach function runs the callback on every element of the array. The map function also runs on each element, but it stores the results of each callback into a new array.

If you want to transform an array by including only certain values, use the filter function.

//find all values that match the filter
var evens = values.filter((x)=>x%2==0);

Array also has functions to find single values within the array based on some criteria.

//find the first that matches
var six = values.find((x) =>  x >= 6);
//find the index of the first that match
var index = values.findIndex((x) =>  x >= 6);
//true if at least one item matches
var any = values.some((x) => x > 10);

Finally arrays can be reduced to a single value with the reduce function.reduce is very powerful and can be used to do lots of things like summing an array or flattening nested arrays

//reduce the array to a single value
var sum = values.reduce((a,b) => a+b, 0);
//flatten nested arrays
var list1 = [[0, 1], [2, 3], [4, 5]];
var list2 = [0, [1, [2, [3, [4, [5]]]]]];
const flatten = arr => arr.reduce(
  (acc, val) => acc.concat(
    Array.isArray(val) ? flatten(val) : val
  ),
  []
);
flatten(list1); // returns [0, 1, 2, 3, 4, 5]
flatten(list2); // returns [0, 1, 2, 3, 4, 5]

To loop over the properties in an object you can use the Object.keys to get an array of property names, then loop over it with forEach

var obj = {
    first:'Alice',
    middle:'N',
    last:'Wonderland',
    age:8,
    height:45,
}
Object.keys(obj).forEach((key)=>{
    console.log("key = ", key);
    console.log("value = ", obj[key]);
});

Things to Avoid

The five features I’ve shown you can be used today, but there’s a lot of other things in ES6 that you should avoid for time being; either because they don’t provide much value or aren’t well supported yet. These include:

  • Destructuring
  • Modules
  • Default Parameters
  • Spread Operator
  • Symbols
  • Generators and Iterators
  • Weak Maps and Weak Sets
  • Proxies

Destructuring allows you to pull values out of an object by name. It can be useful in a few situations, but the best use I’ve found is extracting functions from modules. Unfortunately modules are still a mess and don’t work the same everywhere, so avoid them for now.

Along with destructuring you can do without tricks like default parameters, and the spread operator. I find these to be more trouble than they are worth, at least for now.

Symbols, generators, iterators, weak maps and sets, and proxies are genuinely useful but they aren’t supported everywhere yet so I suggest you wait a while before using them.

There is also a new class syntax. It still uses JavaScript’s prototypical inheritance, but it makes the syntax of defining a class cleaner and consistent. However, it is also not as valuable without the new module support so I suggest waiting a bit longer.

class Foo extends Bar {
    constructor(stuff) {
        super(stuff);
        this.first = "first name"
    }
    doStuff() {
    }
    doMoreStuff() {
    }
}

Can I Use It?

Most desktop and mobile browsers support everything I’ve shown you. However, depending on your audience you may have older browsers / older mobile OSes that don’t. Whenever you want to know if something is ready, go to caniuse.com. It will tell you what versions of each browser support what.

If you need to support IE < 10, there’s a good chance you’ll want to use a transpiler like TypeScript or Babel

So those are five awesome features of ES6 that you can start using right away. More stuff is coming, but you don’t need to wait to use these. Get to codin’. And if you want go use them in low latency data stream driven Javascript code, go check out PubNub BLOCKS.

Deja un comentario