Appendix E. ES6 for success

This appendix provides a quick introduction to ES6. It describes the 10 best features of the new generation of the most popular programming language—JavaScript:

  1. Default parameters
  2. Template literals
  3. Multiline strings
  4. Destructuring assignment
  5. Enhanced object literals
  6. Arrow functions
  7. Promises
  8. Block-scoped constructs: let and const
  9. Classes
  10. Modules
Note

This list if highly subjective. It’s in no way intended to diminish the usefulness of other ES6 features that didn’t make it on the list only because I wanted to limit the number to 10.

Default parameters

Remember when we had to use statements like these to define default parameters?

var link = function (height, color, url) {
    var height = height || 50
    var color = color || 'red'
    var url = url || 'http://azat.co'
    ...
}

Print-ready PDF available

In addition to this essay, I’ve created a free beautifully designed, print-ready ES6/ES2015 cheatsheet. You can request this PDF at http://reactquickly.co/resources.

This approach was fine until the value wasn’t 0. When you have 0, there may be a bug. A 0 value defaults to the hardcoded value instead of becoming the value itself, because 0 is falsy in JavaScript. Of course, who needs 0 as a value (#sarcasmfont)? So we ignored this flaw and used the logical OR. No more! In ES6, you can put the default value right in the signature of the function:

var link = function(height = 50, color = 'red', url = 'http://azat.co') {
  ...
}

This syntax is similar to Ruby. My favorite, CoffeeScript, has this feature, as well—and has had it for many years.

Template literals

Template literals or interpolation in other languages is a way to output variables in a string. In ES5, you had to break the string like this:

var name = 'Your name is ' + first + ' ' + last + '.'
var url = 'http://localhost:3000/api/messages/' + id

In ES6, you can use the new syntax ${NAME} in the back-ticked string:

var name = `Your name is ${first} ${last}.`
var url = `http://localhost:3000/api/messages/${id}`

Do you wonder if you still can use template-literal syntax with Markdown? Markdown uses back-ticks for inline code blocks. That’s a problem! The solution is to use two, three, or more back-ticks for Markdown code that has back-ticks for string templates.

Multiline strings

Another bit of yummy syntactic sugar is the multiline string. In ES5, you had to use one of these approaches:

var roadPoem = 'Then took the other, as just as fair,
	'
    + 'And having perhaps the better claim
	'
    + 'Because it was grassy and wanted wear,
	'
    + 'Though as for that the passing there
	'
    + 'Had worn them really about the same,
	'
var fourAgreements = 'You have the right to be you.

    You can only be you when you do your best.'

In ES6, you can use back-ticks:

var roadPoem = `Then took the other, as just as fair,
    And having perhaps the better claim
    Because it was grassy and wanted wear,
    Though as for that the passing there
    Had worn them really about the same,`
var fourAgreements = `You have the right to be you.
    You can only be you when you do your best.`

Destructuring assignment

Destructuring can be a harder concept to grasp, because some magic is going on. Let’s say you have simple assignments where the keys/objects properties/attributes house and mouse are the variables house and mouse:

Here are some other examples of destructuring assignments (from Node.js):

In ES6, you can replace the previous ES5 code with these statements:

This also works with arrays. Crazy!

var [col1, col2]  = $('.column'),
  [line1, line2, line3, , line5] = file.split('
')

The first line assigns the 0 element to col1 and the 1 element to col2. The second statement (yes, the missing line4 is intentional) produces the following assignment, where fileSplitArray is the result of file.split(' '):

var line1 = fileSplitArray[0]
var line2 = fileSplitArray[1]
var line3 = fileSplitArray[2]
var line5 = fileSplitArray[4]

It may take you some time to get used to the destructuring assignment syntax, but it’s a sweet sugarcoating—no doubt about that.

Enhanced object literals

What you can now do with object literals is mind blowing! We went from a glorified version of JSON in ES5 to something closely resembling classes in ES6.

Here’s a typical ES5 object literal with some methods and attributes/properties:

var serviceBase = {port: 3000, url: 'azat.co'},
    getAccounts = function(){return [1,2,3]}

var accountServiceES5 = {
  port: serviceBase.port,
  url: serviceBase.url,
  getAccounts: getAccounts,
  toString: function() {
    return JSON.stringify(this.valueOf())
  },
  getUrl: function() {return "http://" + this.url + ':' + this.port},
  valueOf_1_2_3: getAccounts()
}

If you want to be fancy, you can inherit from serviceBase by making it the prototype with the Object.create() method:

var accountServiceES5ObjectCreate = Object.create(serviceBase)
var accountServiceES5ObjectCreate = {
  getAccounts: getAccounts,
  toString: function() {
    return JSON.stringify(this.valueOf())
  },
  getUrl: function() {return "http://" + this.url + ':' + this.port},
  valueOf_1_2_3: getAccounts()
}

I know, accountServiceES5ObjectCreate and accountServiceES5 are not identical, because one object (accountServiceES5) has the properties in the proto object (see figure E.1). But for the sake of the example, we’ll consider them similar. In the ES6 object literal, there are shorthands for assignment: getAccounts: getAccounts, becomes just getAccounts, without the colon.

Figure E.1. Objects in ES5

Also, you set the prototype in the __proto__ property, which makes sense (not '__proto__' though—that would be just a property):

var serviceBase = {port: 3000, url: 'azat.co'},
    getAccounts = function(){return [1,2,3]}
var accountService = {
    __proto__: serviceBase,
    getAccounts,

In addition, you can invoke super in toString():

toString() {
     return JSON.stringify((super.valueOf()))
    },
    getUrl() {return "http://" + this.url + ':' + this.port},

And you can dynamically create keys, object properties, and attributes such as valueOf_1_2_3 with the [ 'valueOf_' + getAccounts().join('_') ] construct:

[ 'valueOf_' + getAccounts().join('_') ]: getAccounts()
}
console.log(accountService)

The resulting ES6 object with __proto__ as the serviceBase object is shown in figure E.2. This is a great enhancement to good-old object literals!

Figure E.2. The ES6 object literal extends from serviceBase and defines methods and attributes.

Arrow functions

This is probably the feature I wanted the most. I love the fat arrows in CoffeeScript, and now we have them in ES6. First, arrow functions save space and time because they’re short:

const sum = (a, b, c) => {
  return a + b + c
}

Fat arrows are also amazing because they make this behave properly. this has the same value as in the context of a function—this doesn’t mutate. The mutation typically happens each time you create a closure.

Using arrow functions in ES6 means you don’t have to use that = this, self = this, _this = this, and .bind(this). For example, this code in ES5 is ugly:

var _this = this
$('.btn').click(function(event){
  _this.sendData()
})

This ES6 code is better:

$('.btn').click((event) => {
  this.sendData()
})

Sadly, the ES6 committee decided that having skinny arrows is too much of a good thing, and they left us with a verbose function() instead. (Skinny arrows in CoffeeScript work like the regular function in ES5 and ES6.)

Here’s another example that uses call to pass the context to the logUpperCase() function in ES5:

var logUpperCase = function() {
  var _this = this

  this.string = this.string.toUpperCase()
  return function () {
    return console.log(_this.string)
  }
}

logUpperCase.call({ string: 'es6 rocks' })()

In ES6, you don’t need to mess around with _this:

var logUpperCase = function() {
  this.string = this.string.toUpperCase()
  return () => console.log(this.string)
}
logUpperCase.call({ string: 'es6 rocks' })()

Note that you can mix and match the old function with => in ES6 as you see fit. And when an arrow function is used with a one-line statement, it becomes an expression: that is, it will implicitly return the result of that single statement. If you have more than one line, you need to use return explicitly.

This ES5 code, which creates an array from the messages array,

becomes this in ES6:

Notice that this code uses string templates. Another feature I love from CoffeeScript!

Parentheses (()) are optional for single parameters in an arrow function’s signature. You need them when you use more than one parameter. In ES5, the following code has function() with an explicit return:

And the more eloquent version of the code in ES6 uses parentheses around the parameters and an implicit return:

Promises

Promises have historically been a controversial topic. There were many promise implementations with slightly different syntaxes: Q, Bluebird, Deferred.js, Vow, Avow, and jQuery Deferred, to name just a few. Other developers said we didn’t need promises and could use async, generators, callbacks, and so on. Fortunately, ES6 now has a standard Promise implementation.

Let’s consider a trivial example of delayed asynchronous execution with setTimeout():

setTimeout(function(){
  console.log('Yay!')
}, 1000)

This code can be rewritten in ES6 with Promise:

var wait1000 =  new Promise(function(resolve, reject) {
  setTimeout(resolve, 1000)
}).then(function() {
  console.log('Yay!')
})

It can also use ES6 arrow functions:

var wait1000 =  new Promise((resolve, reject)=> {
  setTimeout(resolve, 1000)
}).then(()=> {
  console.log('Yay!')
})

So far, we’ve increased the number of lines of code from three to five without any obvious benefit. The benefit comes if you have more nested logic in the setTimeout() callback. The following code

setTimeout(function(){
  console.log('Yay!')
  setTimeout(function(){
    console.log('Wheeyee!')
  }, 1000)
}, 1000)

can be rewritten with ES6 promises:

var wait1000 =  ()=> new Promise((resolve, reject)=>
  {setTimeout(resolve, 1000)})
wait1000()
    .then(function() {
        console.log('Yay!')
        return wait1000()
    })
    .then(function() {
        console.log('Wheeyee!')
    });

Still not convinced that promises are better than regular callbacks? Me neither. I think that once you get the idea of callbacks, there’s no need for the additional complexity of promises. Nevertheless, promises are available in ES6 for those who adore them; and they do have a fail-and-catch-all callback, which is a nice feature. See James Nelson’s post “Introduction to ES6 Promises: The Four Functions You Need to Avoid Callback Hell” for more about promises (http://mng.bz/3OAP).

Block-scoped constructs: let and const

You may have already seen the weird-sounding let in ES6 code. It isn’t a sugarcoating feature; it’s more intricate. let is a new var that lets you scope a variable to blocks. You define blocks with curly braces. In ES5, blocks did nothing to variables:

function calculateTotalAmount (vip) {
  var amount = 0
  if (vip) {
    var amount = 1
  }
  { // More crazy blocks!
    var amount = 100
    {
      var amount = 1000
      }
  }
  return amount
}
console.log(calculateTotalAmount(true))

The result is 1,000. Wow! That’s a bad bug. In ES6, you use let to restrict the scope to the blocks. Variables are function-scoped:

function calculateTotalAmount (vip) {
  var amount = 0 // Probably should also be let, but you can mix var and let
  if (vip) {
    let amount = 1 // First amount is still 0
  }
  { // more crazy blocks!
    let amount = 100 // First amount is still 0
    {
      let amount = 1000 // First amount is still 0
      }
  }
  return amount
}
console.log(calculateTotalAmount(true))

The value is 0, because the if block also has let. If it had nothing (amount=1), then the expression would have been 1.

When it comes to const, things are easier; it creates a read-only reference, and it’s block-scoped like let. (Read-only means you can’t reassign the variable identifier.) const works on objects as well; their properties can change.

Suppose you have a constant url, like this: const url="http://webapplog.com". Reassigning it with const url="http://azat.co" will fail in most browsers—although the documentation states that const doesn’t mean immutability, if you try to change the value, it won’t change.

To demonstrate, here’s a bunch of constants that are okay because they belong to different blocks:

function calculateTotalAmount (vip) {
  const amount = 0
  if (vip) {
    const amount = 1
  }
  { // More crazy blocks!
    const amount = 100
    {
      const amount = 1000
      }
  }
  return amount
}
console.log(calculateTotalAmount(true))

In my humble opinion, let and const overcomplicate the language. Without them, we had only one behavior; but now there are multiple scenarios to consider.

Classes

If you love object-oriented programming, then you’ll love this feature. It makes writing classes and inheriting from them as easy as liking a comment on Facebook.

Creating and using classes in ES5 was a pain because there was no class keyword (it was reserved but did nothing). In addition, lots of inheritance patterns like pseudo-classical,[1] classical,[2] and functional just added to the confusion, pouring gasoline on the fire of the JavaScript wars.

1

See Ilya Kantor, “Class Patterns,” http://javascript.info/class-patterns.

2

See Douglas Crockford, “Classical Inheritance in JavaScript,” www.crockford.com/javascript/inheritance.html.

I won’t show you how to write a class (yes, there are classes; objects inherit from objects) in ES5, because there are many flavors. Let’s look at an ES6 example. The ES6 class uses prototypes, not the function factory approach. Here’s a baseModel class in which you can define a constructor and a getName() method:

Notice that this code uses default parameter values for options and data. Also, method names no longer need to include the word function or a colon (:). The other big difference is that you can’t assign properties (this.NAME) the same way as methods—that is, you can’t say name at the same indentation level as a method. To set the value of a property, assign a value in the constructor.

AccountModel inherits from baseModel with class NAME extends PARENT_NAME. To call the parent constructor, you can effortlessly invoke super() with parameters:

If you want to be fancy, you can set up a getter like this, and accountsData will be a property:

How do you use this abracadabra? It’s easy:

let accounts = new AccountModel(5)
accounts.getName()
console.log('Data is %s', accounts.accountsData)

In case you’re wondering, the output is

Class name: Account Model
Data is %s 32113123123,524214691

Modules

As you may know, JavaScript had no support for native modules before ES6. People came up with AMD, RequireJS, CommonJS, and other workarounds. Now there are modules with import and export operands.

In ES5, you use <script> tags with an immediately invoked function expression or a library like AMD, whereas in ES6 you can expose a class with export. I’m a Node.js guy, so I use CommonJS, which is also a Node.js module syntax.

It’s straightforward to use CommonJS on the browser with the Browserify bundler (http://browserify.org). Let’s say you have a port variable and a getAccounts method in an ES5 module.js file:

module.exports = {
  port: 3000,
  getAccounts: function() {
    ...
  }
}

In the ES5 main.js file, you’d require('module') that dependency:

var service = require('module.js')
console.log(service.port) // 3000

In ES6, you use export and import. For example, this is the library in the ES6 module.js file:

export var port = 3000
export function getAccounts(url) {
  ...
}

In the ES6 main.js importer file, you use the syntax import {name} from 'my-module':

import {port, getAccounts} from 'module'
console.log(port) // 3000

Or you can import everything as a service variable in main.js:

import * as service from 'module'
console.log(service.port) // 3000

Personally, I find ES6 modules confusing. Yes, they’re more eloquent, but Node.js modules won’t change anytime soon. It’s better to have only one style for browser and server JavaScript, so I’ll stick with CommonJS/Node.js style for now. In addition, support for ES6 modules in browsers isn’t available as of this writing, so you’ll need something like jspm (http://jspm.io) to use ES6 modules.

For more information and examples, see http://exploringjs.com/es6/ch_modules.html. And no matter what, write modular JavaScript!

Using ES6 today with Babel

To use ES6 today, use Babel as part of your build process. There’s more information on Babel in chapter 3.

Other ES6 features

There are many other noteworthy ES6 features that you probably won’t use (at least, not right away). Here they are, in no particular order:

  • New math, number, string, array, and object methods
  • Binary and octal number types
  • Default rest spread
  • For of comprehensions (hello again, mighty CoffeeScript!)
  • Symbols
  • Tail calls
  • Generators
  • New data structures like Map and Set

ECMAScript improves productivity and reduces mistakes. It will continue to evolve. Learning never stops. Take advantage of these resources:

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.29.219