In the previous article, the template literals were introduced alongside the JavaScript object literal enhancements. This article will discuss the destructuring assignment, the arrow functions syntax and a handy feature; maps. Let's get started.

Destructuring Assignment

It is an expression to extract values from arrays or properties from objects to standalone variables.

A developer coming from Python background would find this feature familiar.

Let's take some examples.

var arr = ['one', 'two', 'three'];

[one, two] = arr;

console.log(one); // 'one'
console.log(two); // 'two'

The value 'one' has been assigned to the variable one, and the value 'two' to the variable two. We can also assign default values to the variables.

var arr = ['zero'];

[one = 'one', two = 'two'] = arr;

console.log(one); // 'zero'
console.log(two); // 'two'

We can also ignore some returned values if not interested in them.

var arr = ['one', 'two', 'three'];

[one, , three] = arr;

console.log(one); // 'one'
console.log(three); // 'three'

We can assign the remaining parts of the array to a variable using the rest pattern (we'll discuss that in a later article when introducing the spread operator).

var arr = ['one', 'two', 'three'];

[one, ...rest] = arr;

console.log(one); // 'one'
console.log(rest); // ['two', 'three']

The same with array destructuring, object can be destructured as well.

var person = {
    name: 'Jack',
    age: 30
};

var {name, age} = person;

console.log(name); // 'Jack'
console.log(age); // 30

Variables can be assigned values with destructuring separated from declarations with a condition.

var a, b;

({a, b} = {a: 1, b: 2});

The ( .. ) around the assignment statement is required syntax when using object literal destructuring assignment without a declaration.
{a, b} = {a: 1, b: 2} is not valid stand-alone syntax, as the {a, b} on the left-hand side is considered a block and not an object literal.
However, ({a, b} = {a: 1, b: 2}) is valid, as is var {a, b} = {a: 1, b: 2}
NOTE: Your ( ..) expression needs to be preceded by a semicolon or it may be used to execute a function on the previous line.

If one needed to use different names for the properties, one could do the following.

var person = {
    name: 'Jack',
    age: 30
};

var {name: theName, age: theAge} = person;

console.log(theName); // 'Jack'
console.log(theAge); // 30

And just like the array destructuring, default values could be assigned to the variables.

var {a = 1, b = 10} = {a: 5};

console.log(a); // 5
console.log(b); // 10

One of the use cases of the object destructuring is setting a function parameter's default value; although there is a feature to do so which will be discussed in another article. MDN has a good example, so I'll borrow it.

ES5 Version

function drawES5Chart(options) {
  options = options === undefined ? {} : options;
  var size = options.size === undefined ? 'big' : options.size;
  var cords = options.cords === undefined ? {x: 0, y: 0} : options.cords;
  var radius = options.radius === undefined ? 25 : options.radius;
  console.log(size, cords, radius);
  // now finally do some chart drawing
}

drawES5Chart({
  cords: {x: 18, y: 30},
  radius: 30
});

ES6/ES2015 Version

function drawES2015Chart({size = 'big', cords = {x: 0, y: 0}, radius = 25} = {}) {
  console.log(size, cords, radius);
  // do some chart drawing
}

drawES2015Chart({
  cords: {x: 18, y: 30},
  radius: 30
});

In the function signature for drawES2015Chart above, the destructured left-hand side is assigned to an empty object literal on the right-hand side: {size = 'big', cords = {x: 0, y: 0}, radius = 25} = {}. You could have also written the function without the right-hand side assignment. However, if you leave out the right-hand side assignment, the function will look for at least one argument to be supplied when invoked, whereas, in its current form, you can only call drawES2015Chart() without supplying any parameters. The current design is useful if you want to be able to call the function without supplying any parameters, the other can be useful when you want to ensure an object is passed to the function.

Arrow Functions

Arrow functions, also known as "fat" functions because they use this =>, utilize a shorter syntax than the regular function expressions and do not bind their own this. Let's see them in some examples.

// ES5 function expression
var getFullName = function (firstName, lastName) {
    return firstName + ' ' + lastName;
}

// ES6 function expression
var getFullNameES6 = (firstName, lastName) => firstName + ' ' + lastName;

Short and to the point. The function keyword has been replaced with => which is taking place after the parameters. We don't need the curly braces anymore (well, in this case at least), and the return keyword has been emitted.

To even shorten it more, if we had one parameter, we could emit the parentheses.

var square = num => num * num;

Pretty handy, eh?

Now let's see an example of no binding of this. Again, I'm going to borrow the example from MDN.

ES5 Version

function Person() {
  // The Person() constructor defines `this` as an instance of itself.
  this.age = 0;

  setInterval(function growUp() {
    // In non-strict mode, the growUp() function defines `this` 
    // as the global object, which is different from the `this`
    // defined by the Person() constructor.
    this.age++;
  }, 1000);
}

var p = new Person();

ES6 Version

function Person(){
  this.age = 0;

  setInterval(() => {
    this.age++; // |this| properly refers to the person object
  }, 1000);
}

var p = new Person();

An arrow function does not create its own this, the this value of the enclosing execution context is used.

Maps

Maps, as one might assume, link a key to a value. It seems like the everyday object. However, there are some differences. Let's see the map in an example first. Over MDN there is a good example.

var myMap = new Map();

var keyString = 'a string',
    keyObj = {},
    keyFunc = function() {};

// setting the values
myMap.set(keyString, "value associated with 'a string'");
myMap.set(keyObj, 'value associated with keyObj');
myMap.set(keyFunc, 'value associated with keyFunc');

myMap.size; // 3

// getting the values
myMap.get(keyString);    // "value associated with 'a string'"
myMap.get(keyObj);       // "value associated with keyObj"
myMap.get(keyFunc);      // "value associated with keyFunc"

myMap.get('a string');   // "value associated with 'a string'"
                         // because keyString === 'a string'
myMap.get({});           // undefined, because keyObj !== {}
myMap.get(function() {}) // undefined, because keyFunc !== function () {}

The differences between objects and maps include:

  • Objects' keys can only be String or Symbol, while maps' keys anything; including NaN.
var myMap = new Map();
myMap.set(NaN, 'not a number');

myMap.get(NaN); // "not a number"
  • Objects are not iterable, while maps are; using forEach and for..of.
myMap.forEach(function(value, key) {
  console.log(key + ' = ' + value);
});
// Will show 2 logs; first with "0 = zero" and second with "1 = one"
var myMap = new Map();
myMap.set(0, 'zero');
myMap.set(1, 'one');
for (var [key, value] of myMap) {
  console.log(key + ' = ' + value);
}
// 0 = zero
// 1 = one

for (var key of myMap.keys()) {
  console.log(key);
}
// 0
// 1

for (var value of myMap.values()) {
  console.log(value);
}
// zero
// one

for (var [key, value] of myMap.entries()) {
  console.log(key + ' = ' + value);
}
// 0 = zero
// 1 = one
  • Objects have some properties/keys already such as toString, constructor, valueOf, hasOwnProperty, isPrototypeOf.
  • Clearing an object will require some code, while maps utilize the clear() method.