Tips For Javascript Developers in 2019

by admin admin Date: 12-02-2019 javascript tips tutorial


Another year is over and JavaScript is constantly changing. However, there are some tips that can help you write in 2019 clean and efficient code that scales. Below is a list of 9 pragmatic suggestions.

async / await

Are you still trapped in callback hell?

Don't use callbacks unless it's completely necessary, such as a library or for performance reasons. Promises are totally fine, but if your codebase gets bigger, they're a little awkward to use.

Today, the solution is to implement async / wait, which makes my code much cleaner to read and improve. In fact, you can wait for every JavaScript Promise if a a library you use returns a promise, just wait for it. Async / await is actually only syntactic sugar around promises. You just need to add the async keyword in front of a function to make your code work.

Here's a fast example:

async function getData() {
    const result = await axios.get('https://dube.io/service/ping')
    const data = result.data
    
    console.log('data', data)
    
    return data
}

getData()

Note that await on the top level is not possible, you can only call an asyncfunction.

async / await was introduced with ES2017, so make sure to transpile your code.

 

async control flow

 

It is often necessary to collect several datasets and do something for each one or complete a task after all async calls have returned a value.

for…of

Let's say we've got a couple of Pokemon on our page, and we need to get detailed information. We don't want to wait until all calls are finished, especially if we don't know how many, but we want to update our data sets as soon as we get something back.

To loop through an array and execute async code within the block, we can use a for...of ; the execution is stopped until each call is successful.

It is important to keep in mind that if you do something like this in your code, there may be performance bottlenecks, but keeping in your tool set is very useful. The following is an example:

import axios from 'axios'

let myData = [{id: 0}, {id: 1}, {id: 2}, {id: 3}]

async function fetchData(dataSet) {
    for(entry of dataSet) {
        const result = await axios.get(`https://ma-no.org/gamers/${entry.id}`)
        const newData = result.data
        updateData(newData)
        
        console.log(myData)
    }
}

function updateData(newData) {
    myData = myData.map(el => {
        if(el.id === newData.id) return newData
        return el
    })
}
    
fetchData(myData)

Promise.all

What if you want to pick up all gamers at the same time? Since all promises can be awaited, just use Promise.all:

import axios from 'axios' 

let myData = [{id: 0}, {id: 1}, {id: 2}, {id: 3}]

async function fetchData(dataSet) {
    const gamersPromises = dataSet.map(entry => {
        return axios.get(`https://ma-no.org/gamers/${entry.id}`)
    })

    const results = await Promise.all(gamersPromises)
    
    results.forEach(result => {
        updateData(result.data)
    })
    
    console.log(myData) 
}

function updateData(newData) {
    myData = myData.map(el => {
        if(el.id === newData.id) return newData
        return el
    })
}
    
fetchData(myData)

Destructuring & default values

Let's go back to our previous example where we do this:

const result = axios.get(`https://ma-no.org/gamers/${entry.id}`)
const data = result.data

We can use destructuring to only take one or some of the values from an object or array. We would like to do it:

const data = result.data

We saved one line of code! Yay! You can also rename your variable:

const { data: newData } = await axios.get(...)

Giving default values when destructing is another nice trick. This guarantees that you never end up undefined and that you don't have to manually check the variables.

const { id = 5 } = {}
console.log(id) // 5

These tricks can also be used with function parameters, for example:

function calculate({operands = [1, 2], type = 'addition'} = {}) {
    return operands.reduce((acc, val) => {
        switch(type) {
            case 'addition':
                return acc + val
            case 'subtraction':
                return acc - val
            case 'multiplication':
                return acc * val
            case 'division':
                return acc / val
        }
    }, ['addition', 'subtraction'].includes(type) ? 0 : 1)
}

console.log(calculate()) // 3
console.log(calculate({type: 'division'})) // 0.5
console.log(calculate({operands: [2, 3, 4], type: 'multiplication'})) // 24

The example may seem somewhat confusing at first, but take your time and play with it. The default values are used if we do not pass any values to our function as arguments. Once we start passing values, for non - existent arguments only default values are used.

Truthy & falsy values

Some checks for existing values are a thing of the past when default values are used. However, it is very nice to know that you can work with true and false values. This improves your code and saves a few instructions to make it more eloquent. I often see people doing something like this:

if(myBool === true) {
  console.log(...)
}
// OR
if(myString.length > 0) {
  console.log(...)
}
// OR
if(isNaN(myNumber)) {
  console.log(...)
}

To really take full advantage of these statements, you have to understand what truthy and false values are. Here's a small overview:

Falsy

  • strings with the length of 0
  • the number 0
  • false
  • undefined
  • null
  • NaN

Truthy

  • empty arrays
  • empty objects
  • Everything else

Note that when checking for truthy / falsy values, there is no explicit comparison, which equates to checking with double equal signs == and not three ===. In general, it behaves the same way but there are certain situations where you will end up with a bug. For me, it happened mostly with the number 0.

Logical and ternary operators

Again, they are used to shorten your code, saving you precious lines of code. Often they are nice tools and help keep your code clean, but they can also create some confusion, especially when chaining them.

Logical operators

The logical operators basically combine two expressions and will return either true, false or the matching value and are represented by && , meaning “and” — as well as ||, meaning “or”. Let’s have a look:

console.log(true && true) // true
console.log(false && true) // false
console.log(true && false) // false
console.log(false && false) // false
console.log(true || true) // true
console.log(true || false) // true
console.log(false || true) // true
console.log(false || false) // false

We can combine logical operators with our knowledge from the last point, truthful and false values. The following rules apply when using logical operators:

  • && : The first falsy value gets returned, if there is none, the last truthy value is being returned.
  • ||: The first truthy value gets returned, if there is none, the operation will equal to the last falsy value.

 

console.log(0 && {a: 1}) // 0
console.log(false && 'a') // false
console.log('2' && 5) // 5
console.log([] || false) // []
console.log(NaN || null) // null
console.log(true || 'a') // true

Ternary operator

The ternary operator is much like the logical operator, but it has three parts:

  • The comparison, which will either be falsy or truthy
  • The first return value, in case the comparison is truthy
  • The second return value, in case the comparison is falsy

Example:

const lang = 'German'
console.log(lang === 'German' ? 'Hallo' : 'Hello') // Hallo
console.log(lang ? 'Ja' : 'Yes') // Ja
console.log(lang === 'French' ? 'Bon soir' : 'Good evening') // Good evening

Optional chaining

Did you ever have the problem of accessing a property of a nested object without knowing whether the object or one of the sub - properties exists? You'll likely end up with something like this:

let data
if(myObj && myObj.firstProp && myObj.firstProp.secondProp && 
myObj.firstProp.secondProp.actualData) data = 
myObj.firstProp.secondProp.actualData

This is boring and tedious and there is a better way, at least a proposed way (continue to read how to make it possible). Optional chaining is called and works as follows:

const data = myObj?.firstProp?.secondProp?.actualData

Class properties & binding

Binding functions in JavaScript is a common task. With the introduction of arrow functions in the ES6 spec, we now have a way to automatically link functions to to the declaration context - very useful and commonly used by developers of JavaScript.

When classes were first adopted, arrow functions could no longer be used because class methods had to be declared in a specific way.

We had to bind functions elsewhere, like in the constructor (best practice with React.js)

We can use arrow functions again with the class property syntax and obtain the advantages of auto - binding.

Arrow function can now be used inside the class. Here is an example with _increaseCount being bound:

class Counter extends React.Component {
    constructor(props) {
        super(props)
        this.state = { count: 0 }
    }
    
    render() {
        return(
            <div>
                <h1>{this.state.count}</h1>  
                <button onClick={this._increaseCount}>Increase Count</button>
            </div>
        )
    }
    
    _increaseCount = () => {
        this.setState({ count: this.state.count + 1 })
    }
}

Use parcel

As a frontend developer, you have almost certainly encountered bundling and transpiling code. The de-facto standard was a webpack for a long time. We used version 1 webpack first. Fiddling with all the different configuration options, we spent countless hours trying to get the bundling up and running.

Parcel does pretty much everything for you out of the box, while still giving you the possibility to change it, if necessary. It also supports a plugin system, similar to webpack or babel and is incredibly fast. If you don’t know parcel, I definitely suggest you check it out!

Write more code on your own

This is a nice subject of discussion. We've had many different conversations about it. Many people even use a component library such as bootstrap for CSS. We still see people for JavaScript using jQuery and small libraries for validation, sliders, etc.

While using libraries may make sense, we strongly recommend that you write more code instead of blindly installing npm packages. When there are large libraries (or even frameworks) where a whole team builds it, such as moment.js or react-datepicker, trying to code it yourself doesn't make sense.

You can write most of the code you use yourself, though. This gives you three main benefits:

1. You know exactly what's going on in your code

2. Sometime you'll begin to really understand programming and how things work under the hood.

3. You prevent the bloating of your codebase

It's easier to use a npm package at first. To implement some functionality on your own, it will take more time. But what if the package doesn't really work as expected and you need to switch to another, spending even more time reading how your API is set up.

You can customize it 100% to your use when you implement it on your own :-).

 
by admin admin Date: 12-02-2019 javascript tips tutorial hits : 2084  
 
 
 
 

Related Posts