5 Wonderful Javascript Tricks
Tags: javascript
Published: 2020-09-30 01:52:45

# Javascript Tricks

## Query Selector

You can get elements through the DOM API with methods like `getElementsByClassName` or `getElementById`, but these can both be replaced with `querySelector`.

`querySelector` works like a CSS selector, using a period `.` to select classes and pounds `#` to select IDs. For example:

```Javascript
const classElement = document.querySelector('.myClass')
const idElement = document.querySelector('#myID')
```

`querySelector` is also able to target sub elements, which is not available to `getElementById` or `getElementsByClassName`. Use the `space` syntax to get the direct child, or the `caret` `>` syntax to get all children.

`querySelectorAll` gets you all of the matches and returns an array of nodes.

```Javascript
// sub elements
const subElement = document.querySelector('.myClass > li')
// return multiple elements
const allDivs = document.querySelectorAll('div')
```

## Array Methods

Some array methods are overrated, and some are underrated:

### `reduce`

`reduce` is underrated, especially by new developers - it takes a while to understand it's full power. `reduce` gives you an accumulator and a value in your callback function, **but the accumulator doesn't have to be a number!** -- It can be anything: an object, an array, a string,

```js
const nums = [1, 2, 3]
const sum = nums.reduce((a, v) => a + v, 0) // Best practice: accumulator value defaults to first element of the object/array, so be sure to specify a default of zero to avoid typeof() errors.
const sum = nums.reduce((a, v) => [a + v], []) // object, [ '123' ]
const sum = nums.reduce((a, v) => a.concat(v), []) // array, [1, 2, 3]
const sum = nums.reduce((a, v) => a + v, '') // string, '123'
```

`reduce` allows you to very easily convert arrays to hashmaps or objects, build up strings, build up a variation of the original array

```js
const veggies = [
    {name: 'potato', price: 1}
    {name: 'onion', price: 2}
    {name: 'cucumber', price: 1}
]
const veggiePrices = veggies.reduce(
    (a,v) => {
        a[v.name] = v.price
        return a
    },
    {}
)
console.log(veggiePrices) // { potato: 1, onion: 2, cucumber: 1 }
// Creates {name: price} object
```

`reduce` is powerful enough to do the job of `map`, `sort`, and `filter` **if you write the right callback function.**

## Destructuring Tricks

Destructuring is (almost) unique to Javascript, and allows you to pull out one (or more) properties from an object, and is heavily used in frameworks such as Angular or React.

```js
const veggie = { name: 'potato', price: 1 }
const { price } = veggie
console.log(price) // 1
```

Destructuring is also used anytime that you're importing libraries:

```js
import { debounce } from 'lodash'
```

Destructuring can also be used as a parameter to a function:

```js
const veggie = { name: 'potato', price: 1 }
function printPrice({ price }) {
  console.log(price)
}
printPrice(veggie) // 1
```

Destructuring can also be nested, going multiple layers deep into an object:

```js
const veggiePrices = {
  potatoes: {
    red: 1,
    gold: 2,
  },
}

const veggiePrices = { potatoes: { red } }
console.log(red) // 1
```

Destructuring can also be used directly after writing an array method. For example, if you want to sort an array and then pull out the first item, you can easily do that with destructuring

```js
const nums = [3, 1, 2]
const [first] = nums.sort()
console.log(first)
```

## Promise Tips

Promises are asynchronous because they run in the background relative to the code you see on the screen.

```js
const res = fetch('https://google.com')
const res2 = fetch('https://facebook.com')
const res3 = fetch('https://instagram.com')
```

Promises can be treated like any other object and store them in data structures.

```js
const URLs = ['https://google.com', 'https://facebook.com', 'https://instagram.com']
```

You can use the map function to create an array of promises:

```js
// OK Practice:
const requests = URLs.map((url) => fetch(url))
```

But do we want the promises to run at the same time or one after the other?

To run them at the same time, use the `Promise.all()` function. This ties all of the promises together, such that **the array resolves after all of the promises resolve**. The result is an array of the results, **some of which may be failures**, so this will also handle your errors.

```js
const responses = Promise.all(requests)
```

If you want the promises to run one after another, you need to be careful!

You can use `async await`, but keep in mind your `awaits` need to be **inside** an `async` function.

```js
// Better Practice - Make it an async IIFE!
(async () => {
  const requests = URLs.map((url) => fetch(url))
  const responses = Promise.all(requests)
})()
```

You also can't run an `await` inside an array method! So if you tried to `map()` by using an `await` inside of a `map()` or `forEach()`, it's not going to work.

To fix this, you can write `await` inside a `map` function, you just need to make it an `async map` function.

```js
// !!! Won't work !!!
(async () => {
  const requests = URLs.map(url => await fetch(url))
})()

// !!! Works !!!
(async () => {
  const requests = async URLs.map(url => await fetch(url))
})()
```

In order to get this to work, you can to use a `for(){}` or `forOf(){}` loop.

```js
(async () => {
  const responses = []
  for (let url of URLs) {
    const res = await fetch(url)
    responses.push(res)
  }
})()
```

## Error Handling

Error handling is made super easy with a `try{} catch(){}` block.

```js
try {
  // code that might throw Error
} catch (err) {
  // what do if Error is thrown
}
```

You can also handle errors with a `.catch()` on a promise. For example, when using a `.then()` chain, you can append a `.catch()` to handle errors that might have occured along the way.

```js
fetch('https://google.com')
  .then((res) => {
    res
      .json()
      .then((json) => {
        console.log('got json', json)
      })
      .catch((err) => console.error('json failed'))
  })
  .catch((err) => console.error('request failed'))
```

If using `async await` on the above example, we can either throw an `await` in a `try catch` block, **OR** we can append a `.catch()` on the end of the `await` syntax. This is much cleaner than the above example, because we're combining traditional promise syntax with `await`.

> If you have the same error handler for both `await`, you can just wrap both `await` in a `try` block, and handle both errors in the corresponding `catch` block.

```js
(async () => {
  const res = await fetch('').catch((err) => console.error(err))
  const json = await res.json().catch((err) => console.log(err))
  console.log('got json', json)
})()
```