Modern JavaScript features which you should be using every day for better development and what problems do they solve.

Published Apr 25, 2018Last updated May 22, 2018
Modern JavaScript features which you should be using every day for better development and what problems do they solve.

The times when JavaScript was only used for adding a little bit of interaction on the website are long gone. New EcmaScript standards are now released every year, bringing more and more useful and powerful features, and JavaScript is not only used on Front-End anymore as one can now also build back-end architecture, mobile, desktop or IOT apps.

This is the first article of the series which I am planning to create. I want to share with you a few of EcmaScript features which I am using on daily basis to improve the efficiency of my development, write a better and easier to read code, as well as why I think that they are just awesome. I will also explain how they can be used and what problems do they solve.

Let / Const

Since I started to use 'let' and 'const' I have never again used 'var' keyword. 'let' was introduced as a replacement for 'var'. Both 'let' and 'const' are block-scoped, while 'var' is function scoped. What it means is that if we for example initialize a 'var' inside a for loop it will also be accessible outside of it.

var animals = ['chicken', 'duck', 'cow'];
for (var key in animals) {
  var animal = animals[key];
}

console.log(animal); // cow

As you can see, we declared 'animal' variable inside the loop, but we can still access it outside of it. However, if we use 'let' instead, we will get a Reference error that 'animal' is not defined.

const animals = ['chicken', 'duck', 'cow'];

for (var key in animals) {
  let animal = animals[key];
}

console.log(animal); // Reference error

You may ask why would we even bother with this and not use 'var' anymore. Well, in the loop we might need to declare variables to store some data temporarly, which is only needed for that loop. Therefore, there is no need for these variables to be available outside in the outer scope. If you need to access a certain variable outside of the for loop, you can just declare the variable outside.

const animals = ['chicken', 'duck', 'cow'];
let animal
for (var key in animals) {
  animal = animals[key];
}
console.log(animal); // cow

Also, maybe you spotted that in these examples I also changed 'var animals = []' to 'const animals = []'. It is beneficial to use a 'const' if the value assigned should not be changed in the future. So, if you declare it with 'var', you can change it to String, Object, Number, etc.

var animals = ['chicken', 'duck', 'cow'];

animals = {
  animal1: 'cow',
  animal2: 'chicken'
}

console.log(animals); // Object {animal1: "cow", animal2: "chicken"}

However, if you assign a value to 'const' and try to change it, you will get an error.

const animals = ['chicken', 'duck', 'cow'];

animals = {
  animal1: 'cow',
  animal2: 'chicken'
}

console.log(animals); // SyntaxError: animals is read-only

It is important to remember that even if you assign an object or array to a 'const', you still can change their inner properties and values!

const animals = ['chicken', 'duck', 'cow'];
animals[0] = 'pig';
console.log(animals); // ["pig", "duck", "cow"]

Very interesting feature of 'let' is that each time it is declared, it creates a new fresh binding for the scope. Imagine that you need to loop through some kind of data and on every loop you need to make an api call and then use an index value from the loop in the callback.

for (var i = 0; i < 5; i++) {
  axios.get('https://swapi.co/api/people/1')
    .then(resp => {
      console.log(i); // 5
    })
}

In this case, console.log will always be 5. This is because 'for loop' is not waiting for an api call to finish and when the first response does come back, loop has already finished. You can also see that on the image below.

loop.png

One of possible fixes for this is to create a closure for each loop so we can keep the index of the loop for each api call. We can use Immediately Invoked Function Expression (IIFE), to which we will pass our index. This is a function which will call itself and run immediately.

loop2.png

Fortunately, with 'let' we don't need to do that. We can just declare 'i' in the loop with 'let' keyword instead of 'var' and this will fix it for us.

loop3.png

Template literals

I still remember when if I had to create a dynamic markup I had to write a lot of unnecessary (but back then unfortunately necessary) code which also was hardly readable. Just have a look at this:

literals.png

Instead, now we can use template literals to write much better and more readable code. What we are doing here is called String Interpolation. Instead of single or double quotes we use backticks and inside them we can use dolar sign with brackets ${} to add a value from a variable to the string. Thanks to that, our code is much cleaner and easier to read as well as it can span over multiple lines. Just make sure not to over use it. Sometimes it is still a good idea to just do a normal string concatenation with variables.

let result = `${variable} / ${variable} / ${variable}`

let result = variable + ' / ' + variable + ' / ' + variable

Arrow functions

I am using arrow functions all the time and to be honest I use normal function declaration only when I must. There are a few things about Arrow functions which are very important to remember. First of those is the fact that arrow function in comparison to normal functions doesn’t have a lexical scope. What it means is that 'this' will refer to different objects in arrow and normal functions.

function Person (age) {
  this.age = age

  function getAge() {
    console.log('age', this.age) //undefined
    return this.age
  }

  return {
    getAge
  }
}

const Myke = new Person(25);
console.log(Myke.getAge()); // undefined

In this case as we can see, trying to access 'age' on 'this' will result in an error, because 'this' doesn't refer anymore to 'Person' object, but to getAge function itself. Before, usually we had to assign 'this' to a variable like 'var self = this' and then use it in callbacks. However, if we use an arrow function, we will be able to access the 'age' property.

const getAge = () => {
  console.log('age', this.age) // 25
  return this.age
}

Of course I know that we could just assign getAge as a property directly on the object and then we would have access to 'age', but this was done just for the demonstration purpose. Arrow functions are especially useful for callbacks and functions like filter or map, when trying to access a property from an outer scope.

Besides 'this' keyword, 'arguments' are also not binded like they are in a normal function. If we use a normal function we can access arguments.

function vegetables (name, colour) {
  console.log(arguments); // Object {0: "carrot", 1: "orange"}
};

vegetables('carrot', 'orange'); 

However, when we try to access arguments in an arrow function we will not get anything.

const vegetables = (name, colour) => {
  console.log(arguments); // No object
};

vegetables('carrot', 'orange'); //

Another feature of arrow functions which I really like is an immediate return expression. We can omit function brackets and immediately return a value:

const sum = (x, y) => x + y;
console.log(sum(4, 5)); // 9

Also, if we need to pass only one parameter, then we can drop parenthesses as well:

const multiplyByTwo = num => num * 2;
console.log(multiplyByTwo(2)); // 4

However, if we want to immediately return an object, we will not be able to do it just like that, as brackets used for an object are also function brackets.

const getObj = obj => {prop1: obj.prop1, prop2: obj.prop2} // won't work

What we need to do in this case to make it work is to wrap object brackets in parenthesses.

const getObj = obj => ({prop1: obj.prop1, prop2: obj.prop2}) // will work

Arrow functions do have props and cons, but it is one of my most favourite feature and when used well your code can become much clearer and easier to understand.

Default parameters

Usually, if we had to pass a parameter to the function and then do something based on it, we had to add checks if value exists. For instance, if we had to pass an object to the function and then we tried to access a property on it, if nothing was passed then we would have an error.

function doSomethingWithObject(object) { // nothing passed
    if (object && typeof object === 'object') {
        console.log(object.property) // will not fire as we check if object is truthy and is an object
    }

    console.log(object.property) // error!
}

doSomethingWithObject()

Fortunately, now we can define default parameters which do save as all the struggles with parameter checking.

function doSomethingWithObject(object = {}) {};

We can also put more default values or assign a string, integer or anything else we need.

function doSomethingWithObject(object = {property: 'hello'}, text = 'world') {
    console.log(object.property) // 'hello'
    console.log(text) // 'world'
};

doSomethingWithObject();

Property shorthands

Property shorthands is a feature I really like to use. What it does is that if you want to assign a variable to an object property that has the same name, then instead of doing this:

let dinner = 'soup'
mealPlan = {
    dinner: dinner
}

You can make it shorter by writing a 'dinner' only once.

mealPlan = {
    dinner
}

Property shorthands are very useful with destructuring. Destructuring is about extracting properties of an array or object. For instance, in the example below, we destructure data property directorly from the results returned by axios

axios.get('something').then(({data}) => {
  console.log(data); //data property from response object
}

The reason why I like it with destructuring is because if I have a big object with many different properties that I need to prepare for example for an api call, then I can pass this object to a function which will return modified object.

let hugeObject = {'many properties here'}

const prepareData = ({prop1, prop2, prop3, prop4}) => ({
    prop1,
    prop2,
    prop3: prop3 ? 1 : 0,
    prop4
})

You can also rename destructured properties:

const prepareData= ({prop1: name, prop2: surname}) {};

The above are just a few of EcmaScript features which I am using on a daily basis and I hope you will find them as useful as I do. I will continue on more features in the next articles of this series. I hope you enjoyed reading and I invite you all to share your thoughts and tips for a better development.

Discover and read more posts from Thomas Kotowicz
get started