DEV Community

Cover image for 10 JavaScript Tricky Hacks - Part 2
Sayuj Sehgal
Sayuj Sehgal

Posted on

10 JavaScript Tricky Hacks - Part 2

In this second part of our JavaScript Tricky Hacks series, we're back with even more cool tips to elevate your coding skills! Get ready to learn hidden features and shortcuts that will make your JavaScript code cleaner, more efficient, and downright impressive. Let's dive in!

1. Tagged Template Literals

Tagged Template Literals are a more advanced form of Template Literals in JavaScript. They allow you to parse template literals with a function. The first argument of a tag function contains an array of string values. The remaining arguments are related to the expressions.

Here's a simple example:

function tag(strings, ...values) {
  console.log(strings);
  console.log(values);
}

let name = "John";
let age = 30;

tag`Hello, my name is ${name} and I am ${age} years old.`;
Enter fullscreen mode Exit fullscreen mode

In this example, the tag function is our tagged template. When we call this function with a template literal, it gets split into an array of strings and values. The strings array contains all the string parts of the template literal ("Hello, my name is ", " and I am ", " years old."), and the values array contains the evaluated expressions (name, age).

The output of this code would be:

[ 'Hello, my name is ', ' and I am ', ' years old.' ]
[ 'John', 30 ]
Enter fullscreen mode Exit fullscreen mode

This feature is useful when you want to create a function for generating a specific type of text output, with complex rules for how to insert the values into the strings.

2. The Set Object for Unique Elements

In JavaScript, the Set object is a built-in object that stores unique values of any type, whether primitive values or object references. A value in the Set may only occur once; it is unique in the Set's collection.

Here's a simple example of how to use a Set:

let mySet = new Set();

mySet.add(1); // Add a number
mySet.add('some text'); // Add a string
mySet.add('some text'); // Try to add the same string again

console.log(mySet);
Enter fullscreen mode Exit fullscreen mode

In this example, we create a new Set and add a number and a string to it. When we try to add the same string again, it doesn't get added because it's already in the Set. The output of the console.log statement would be:

Set(2) { 1, 'some text' }
Enter fullscreen mode Exit fullscreen mode

This shows that the Set contains two elements: the number 1 and the string 'some text'. The duplicate string was not added to the Set.

You can also create a Set from an array, and it will automatically remove any duplicate values:

let array = [1, 2, 3, 4, 4, 5, 5, 5];
let mySet = new Set(array);

console.log(mySet); // Output: Set(5) { 1, 2, 3, 4, 5 }
Enter fullscreen mode Exit fullscreen mode

In this example, the array contains duplicate numbers. When we create a Set from this array, it automatically removes the duplicates, so the Set contains only the unique numbers from the array.

3. Using Object.entries() and Object.fromEntries()

a) Object.entries()

Object.entries() is a method that returns an array of a given object's own enumerable string-keyed property [key, value] pairs, in the same order as that provided by a for...in loop. The only important difference is that a for...in loop enumerates properties in the prototype chain as well.

Here's an example:

let obj = { foo: 'bar', baz: 42 };
console.log(Object.entries(obj)); // Output: [ ['foo', 'bar'], ['baz', 42] ]
Enter fullscreen mode Exit fullscreen mode

In this example, Object.entries() is used to convert the object into an array of key-value pairs.

b) Object.fromEntries()

Object.fromEntries() performs the reverse operation of Object.entries(). It transforms a list of key-value pairs into an object.

Here's an example:

let entries = [ ['foo', 'bar'], ['baz', 42] ];
let obj = Object.fromEntries(entries);
console.log(obj); // Output: { foo: 'bar', baz: 42 }
Enter fullscreen mode Exit fullscreen mode

In this example, Object.fromEntries() is used to convert the array of key-value pairs back into an object.

4. Dynamic Property Names in Objects

In JavaScript, you can create dynamic property names in objects using computed property names. This is done by wrapping an expression in square brackets [] that gets computed as the property name. This can be very useful when you need to create a property name dynamically.

Here's an example:

let dynamicKey = 'name';

let obj = {
  [dynamicKey]: 'John Doe'
};

console.log(obj); // Output: { name: 'John Doe' }
Enter fullscreen mode Exit fullscreen mode

In this example, the variable dynamicKey is used as the property name of the object obj. The value of dynamicKey is 'name', so the object ends up with a property called 'name' with the value 'John Doe'.

You can also use this feature to create properties with more complex names:

let prefix = 'prop';

let obj = {
  [`${prefix}_name`]: 'John Doe',
  [`${prefix}_age`]: 30
};

console.log(obj); // Output: { prop_name: 'John Doe', prop_age: 30 }
Enter fullscreen mode Exit fullscreen mode

In this example, the property names are created by concatenating the prefix variable with '_name' and '_age'. The resulting object has properties 'prop_name' and 'prop_age'.

5. Function Currying Using bind()

Function currying is a technique in JavaScript where a function with multiple arguments is transformed into a sequence of functions, each with a single argument. This can be achieved using the bind() method.

Here's an example:

function multiply(a, b) {
  return a * b;
}

let multiplyByTwo = multiply.bind(this, 2);

console.log(multiplyByTwo(4)); // Output: 8
Enter fullscreen mode Exit fullscreen mode

In this example, the multiply function takes two arguments. We create a new function multiplyByTwo by calling bind() on multiply. The first argument to bind() is the this value, which we don't need to change, so we pass this. The second argument is the first argument to multiply, which we fix as 2. Now multiplyByTwo is a function that takes a single argument, multiplies it by 2, and returns the result.

6. Using Array.from() to Create Arrays from Array-like Objects

In JavaScript, Array.from() is a static method that creates a new, shallow-copied array instance from an array-like or iterable object.

Here's an example:

let arrayLike = { length: 2, 0: 'a', 1: 'b' };

let arr = Array.from(arrayLike);

console.log(arr); // Output: ['a', 'b']
Enter fullscreen mode Exit fullscreen mode

In this example, arrayLike is an object that has properties and length similar to an array, but it's not an array. Array.from() is used to convert this array-like object into a real array.

Array.from() is also useful when you want to create an array from a string:

let str = 'hello';

let arr = Array.from(str);

console.log(arr); // Output: ['h', 'e', 'l', 'l', 'o']
Enter fullscreen mode Exit fullscreen mode

In this example, Array.from() is used to convert the string into an array of individual characters.

7. The for…of Loop for Iterable Objects

The for...of loop is a JavaScript statement that allows you to iterate over iterable objects, such as arrays, strings, maps, sets, and so on. It's a more concise and readable alternative to traditional for loops.

Below are some of the code examples

a) You can use the for...of loop to iterate over an array:

let fruits = ['apple', 'banana', 'cherry'];

for (let fruit of fruits) {
  console.log(fruit);
}
Enter fullscreen mode Exit fullscreen mode

In this example, the for...of loop iterates over each element in the fruits array and logs it to the console. The output would be:

apple
banana
cherry
Enter fullscreen mode Exit fullscreen mode

b) You can also use the for...of loop to iterate over a string:

let greeting = 'Hello';

for (let char of greeting) {
  console.log(char);
}
Enter fullscreen mode Exit fullscreen mode

In this example, the for...of loop iterates over each character in the greeting string and logs it to the console. The output would be:

H
e
l
l
o
Enter fullscreen mode Exit fullscreen mode

Remember, the for...of loop works with iterable objects, which means it won't work with regular objects unless they are made iterable.

8. Using Promise.all() for Concurrent Promises

Promise.all() is a method in JavaScript that is used to handle multiple promises concurrently. It takes an iterable of promises as an input, and returns a single Promise that resolves when all of the input promises have resolved, or rejects with the reason of the first promise that rejected.

Here's an example:

let promise1 = Promise.resolve(3);
let promise2 = 42;
let promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3]).then((values) => {
  console.log(values); // Output: [3, 42, "foo"]
});
Enter fullscreen mode Exit fullscreen mode

In this example, Promise.all() is used to handle three promises concurrently. promise1 is a Promise that resolves with a value of 3, promise2 is a non-Promise value 42, and promise3 is a Promise that resolves with a value of 'foo' after 100 milliseconds.

Promise.all([promise1, promise2, promise3]) returns a new Promise that resolves when all of the input promises have resolved. The resolved value is an array of the resolved values of the input promises, in the same order as the input promises. In this case, the resolved value is [3, 42, "foo"].

If any of the input promises rejects, then the Promise returned by Promise.all() immediately rejects with the reason of the first promise that rejected, without waiting for the other promises to resolve or reject.

9. The Rest Parameter for Function Arguments

The rest parameter syntax allows us to represent an indefinite number of arguments as an array. This can be particularly useful when writing a function that can accept any number of arguments.

Here's an example:

function sum(...args) {
  let total = 0;
  for(let arg of args) {
    total += arg;
  }
  return total;
}

console.log(sum(1, 2, 3, 4)); // Output: 10
console.log(sum(1, 2, 3, 4, 5)); // Output: 15
Enter fullscreen mode Exit fullscreen mode

In this example, the sum function uses the rest parameter syntax (...args) to collect all of its arguments into an array. It then uses a for...of loop to iterate over the array and add up all of the numbers. The function can be called with any number of arguments, and it will return the sum of all of the arguments.

10. Memoization for Performance Optimization

Memoization is a programming technique used primarily to speed up computer programs by storing the results of expensive function calls and reusing them when the same inputs occur again. This technique is used in optimization problems where the same subproblems are solved again and again.

Here's an example of how you might use memoization in JavaScript to optimize a recursive function for calculating Fibonacci numbers:

let memo = {};

function fib(n) {
  if (n <= 1) {
    return n;
  } else if (memo[n]) {
    return memo[n];
  } else {
    memo[n] = fib(n - 1) + fib(n - 2);
    return memo[n];
  }
}

console.log(fib(10)); // Output: 55
console.log(fib(20)); // Output: 6765
Enter fullscreen mode Exit fullscreen mode

In this example, the fib function first checks if the result for the given input n is already stored in the memo object. If it is, the function returns the stored result, avoiding the need to perform the expensive recursive calls. If the result is not yet stored, the function calculates it using the recursive formula for Fibonacci numbers, stores it in the memo object, and then returns it. This way, the function avoids recalculating the same Fibonacci numbers over and over again, which significantly improves its performance for large inputs.

If you like this blog, you can visit my personal blog sehgaltech for more content.

Top comments (2)

Collapse
 
tylerjrbuell profile image
Tyler Buell

Great article, I didn't know about Tagged Template Literals before. This is a great summary of some useful but lesser-known JS features!

Collapse
 
sayuj profile image
Sayuj Sehgal

Thanks! 😊