DEV Community

Cover image for Javascript Pro Tips
Bogdan Jiang
Bogdan Jiang

Posted on • Updated on

Javascript Pro Tips

In this article, I am going to talk about Javascript Pro Tips. Please learn how to write solid modern Javascript and avoid bad code from the olden days.

Debugging

Actually it’s about console logging stuff.

How do you use console log? There are good ways and bad ways to console log. Imagine you have three different objects. Each one assigned to its own variable like this.

const foo = { name: 'tom', age: 30, nervous: false } 
const bar = { name: 'dick', age: 40, nervous: false }
const baz = { name: 'harry', age:50, nervous: false }

The obvious way to log these is just one after the other.
For example:

console.log(foo);
console.log(bar);
console.log(baz);

But the main problem is we don’t know the name of the variable when this gets logged but there’s a trick we can use here called computed property names where we add the variables to an object.

So you can do like this:

console.log({ foo, bar, baz });

This does not only reduce the code footprint but it also tells us exactly which variable define this data. One line of code, one console log and all the information we need.

But maybe this data is extra important so we want to make it stand out with some custom CSS styling. You can substitute data and also CSS styles by using % sign.

So we’ll add %c and then have the second argument be our actual CSS style.
For example:

console.log('%c My Friends', 'color: orange;')
console.log({ foo, bar, baz });

One thing you might have noticed is that the objects all share common properties so maybe we should display those as a table.

console.table([foo, bar, baz]);

This is really useful when you have an array of objects. You just do console table with the array.
If you are benchmarking performance you can actually keep track of time in the console.

console.time('looper');
let i = 0;
while (i < 1000000) { i ++ }
console.timeEnd('looper')

Keeping track of time is great but what if you need to know where a console.log originated from.

Let’s imagine that we have a really important function that deletes items from our database and we want to make sure that we don’t accidentally call this method twice.

In this case you can add console trace to your function and it will give you a stack trace for where it was called and what defined it if we run this code, we’ll get a console log that tells us the function was defined on which number of line.
For example, we can write:

const deleteMe = () => console.trace('bye bye database')

Destructuring

Here, I am going to show you a few different ways you can make your code as concise and efficient as possible.

Let’s imagine we have an object with some animal data and we need a function that will tell us how to feed the animal.
For example:

function feed(animal) {
    return `Feed ${animal.name} ${animal.meal} kilos of ${animal.diet}`
}

This doesn’t look too bad but you’ll notice that we’re repeating the word animal over and over again. There’s a technique called object destructuring that we can use to eliminate most of the repetition here.

If we have a function that takes an object but we only need to use a handful of its properties. We can destrcture those in the argument itself. We just wrap it in brackets and then pass in the names of the object properties that we want to use.

function feed({ name, meal, diet }) {
    return `Feed ${name} ${meal} kilos of ${diet}`
}

So now we can format the same string but we never actually have to use the word animal directly. this might seem like a modest gain on this simple function but when you have a big object with a lot of properties this can make a huge difference.

If you don’t like this bracket syntax in the object argument, there is actually another way we can do this which is just as good.

function feed(animal) {
    const { name, meal, diet } = animal;
    return `Feed ${name} ${meal} kilos of ${diet}`
}

Now you can use properties like variables throughout the function and this tends to be the better way to go if you have multiple objects 2d structure and a single function.

Template Literals

In this part, I am going to talk about template literals which we’ve already been using in the code. But actually there’s more to talk about here.

For example if we have:

const horse = {
   name: 'Topher',
   size: 'large',
   skills: ['jousting', 'racing'],
   age: 7
}
let bio = horse.name + ' is a ' + horse.size + ' horse skilled in ' + horse.skills.join(' & ')

You would see a lot of string concatenation that looks like above where you have a variable plus string and you have to manage the spaces in between plus an expression plus a whole bunch of other stuff.
This type of code is incredibly annoying to deal with but template literals in modern Javascript solve this problem completely.
Instead of concatenating values together, we can actually interpolate them directly into the string.

You can do this by defining your string with backticks and then use dollar sign brackets and then whatever variable or expression you want inside up there. Like this:

const horse = {
   name: 'Topher',
   size: 'large',
   skills: ['jousting', 'racing'],
   age: 7
}
bio = `${name} is a ${size} skilled in ${skills.join(' & ')}`

This will look a lot more readable and a lot easier to maintain.
But you can actually take things a step further and build strings in a purely functional way. So we’ll write a function here called horse age that takes in array of strings as the first argument and then it can take whatever other arguments that wants after that.
Imagine we have this function:

function horseAge(str, age) {
   const ageStr = age > 5 ? 'old' : 'young';
   return `${str[0]}${ageStr} at ${age} years`;
}

But the interesting thing here is that instead of passing a regular argument to this function we can actually just attach it to a template literal and it will parse the arguments in it.
For example:

const bio2 = horseAge`This horse is ${horse.age}`;

Then it will handle all the other arguments in the order in which they appear inside of the dollar sign brackets. In other words you can take a single argument and use it to compose multiple values in the return string.
This can be a very powerful concept for templating.

Spread Syntax

Let’s imagine we have one object for a Pokemon and the other one for the stats that define its various attributes.

const pikachu = { name: 'Pikachu' }
const stats = { hp: 40, attack: 60, defense: 45 }

Let’s say we want to assign the properties from the stats object to the Pikach object. One way to do that is to just redefine them one by one on the original Pikachu object.

pikachu['hp] = stats.hp
pikachu['attack'] = stats.attack
pikachu['defense'] = stats.defense

For one, this is just really ugly and verbose but we’re also mutating the original object when we most likely want to create a new immutable object because let’s say that our Pokemon levels up over time, we want to represent each level up as its own object.
We could use Object.assign() here and take the original object and merge it in with the stats and this will merge them together from left to right.

const lv10 = Object.assign(pikachu, stats)
const lvl1 = Object.assign(pikachu, { hp: 45 })

This isn’t too bad but there’s a more concise way to do this with the spread syntax. By creating a new object and placing our existing objects in it with three dots in front of them.
For example:

const lvl0 = { ...pikachu, ...stats }
const lvl1 = { ...pikachu, hp: 45 }

This will compose a new object from left to right. So the property is farthest to the right will have the priority. Again this is mostly just syntactic sugar and it just makes your code more readable and easier to maintain.
And it’s also possible to use the spread syntax on arrays. Let’s imagine we have an array of strings and we need to push additional items to this array.

let pokemon = ['Arbok', 'Raichu', 'Sandshrew']

The old school way to do this would be to just push new items to the array one by one. For example:

pokemon.push('Bulbasaur')
pokemon.push('Metapod')
pokemon.push('Weedle')

But in today’s world, we can reduce these three lines of code to just one by defining an array with the new items and in the spread syntax on the original array. Like this:

pokemon = [...pokemon, 'Bulbasaur', 'Metapod', 'Weedle']
pokemon = ['Bulbasaur', ...pokemon, 'Metapod', 'Weedle']

Loops

Let’s imagine that we have an array of numbers here that represent the other totals.

const orders = [500, 30, 99, 15, 223]

Now we need to compute some values based on this array such as the order total. Maybe we need to add some tax to each one and filter out the high value orders to be reviewed by a manager. One option is to just use a classic for loop like you’ll find in pretty much every programming language. For example:

const total = 0;
const withTax = [];
const highValue = [];
for(i = 0; i < orders.length; i ++) {
  // Reduce
  total += orders[i];
  // Map
  withTax.push(orders[i] * 1.1);
  // Filter
  if (orders[i] > 100) {
    highValue.push(orders[i])
  }
}

This code is very ugly and it’s mutating values might make our code a little more unpredictable. Luckily we can reduce this down to just three lines of code by using modern Javascript array methods. Like this:

// Reduce
const total = orders.reduce((acc, cur) => acc + cur)
// Map
const withTax = orders.map(v => v * 1.1)
// Filter
const highValue = orders.filter(v => v > 100)

async / await

Let’s create a method called random that returns a promise that resolves to a random number asynchronously.

const random = () => {
  return Promise.resolve(Math.random())
}

Now we want to retrieve three different asynchronous numbers one after the other and then add them all together at the end. That might seem like a silly example but that’s actually how things work a lot of times in the real world when you have to retrieve one item from the database get some data retrieving another item from an API and so on.
With promises you wait for an asynchronous value to resolve and then you handle it with a callback function inside of then. For example:

const sumRandomAsyncNums = () => {
  let first;
  let second;
  let third;
return random()
       .then(v => {
           first = v;
           return random();
       })
       .then(v => {
           second = v;
           return random();
       })
       .then(v => {
           third = v;
           return first + second + third;
       })
}

We can rewrite our promise chain. The only difference is adding async in front of the function which will force it to return a promise. Like this:

const sumRandomAsyncNums = async () => {

    const first = await random();
    const second = await random();
    const third = await random();
    return first + second + third;
}

The real benefit here is that we can use a await in front of our promises and have them resolve to an actual variable value. So instead of using those then callbacks we can just say

const first = await random();

and do the same thing for the second and third number. Now it’s much easier to read and understand this code because we can just go line by line and see that we’re waiting one number awaiting another number and so on.

This async/await concept is one of the most awesome things to ever happen to Javascript!

That’s it for today.

Thanks for you reading!

Top comments (0)