DEV Community

Cover image for Hidden Gems: Lesser-Known Features in JavaScript
Jonas Pfalzgraf
Jonas Pfalzgraf

Posted on

Hidden Gems: Lesser-Known Features in JavaScript

JavaScript is undoubtedly one of the most powerful and widely-used programming languages in the world. Most developers are familiar with the basic features, but there are a plethora of hidden gems that often go unnoticed. In this article, we will explore some of these rarely used functions and how they can make the development process more efficient and elegant.

1. Object.getOwnPropertyDescriptors

The Object.getOwnPropertyDescriptors function allows you to retrieve all property descriptors of an object. This can be particularly useful when you want to copy properties or clone objects without merely duplicating the values.

const source = { name: 'Alice', age: 30 };
const descriptors = Object.getOwnPropertyDescriptors(source);

const target = Object.create(null);
for (const key in descriptors) {
  Object.defineProperty(target, key, descriptors[key]);
}

console.log(target); // { name: 'Alice', age: 30 }
Enter fullscreen mode Exit fullscreen mode

2. Array.from with Mapping Function

The Array.from function is often used to create arrays from iterable objects. But did you know you can also pass a mapping function?

const numbers = [1, 2, 3, 4];
const squared = Array.from(numbers, num => num * num);

console.log(squared); // [1, 4, 9, 16]
Enter fullscreen mode Exit fullscreen mode

3. String.prototype.trimStart and String.prototype.trimEnd

Removing leading or trailing spaces from a string is a common task. With the trimStart and trimEnd functions, this becomes a breeze.

const text = '   Hello, World!   ';
const trimmed = text.trimStart().trimEnd();

console.log(trimmed); // 'Hello, World!'
Enter fullscreen mode Exit fullscreen mode

4. Intl.Collator for Natural String Sorting

The Intl.Collator function allows for correct natural sorting of strings in different languages and cultures.

const words = ['äpfel', 'Zebra', 'Bär', 'Apfel', 'über'];
const collator = new Intl.Collator('de');

const sorted = words.sort(collator.compare);
console.log(sorted); // ['Apfel', 'äpfel', 'Bär', 'über', 'Zebra']
Enter fullscreen mode Exit fullscreen mode

5. Promise.allSettled

While Promise.all aborts on an error, Promise.allSettled returns results for all passed promises, regardless of whether they were fulfilled or rejected.

const promises = [
  Promise.resolve('Success'),
  Promise.reject('Error'),
  Promise.resolve('Another success')
];

Promise.allSettled(promises)
  .then(results => console.log(results))
  .catch(error => console.error(error));
Enter fullscreen mode Exit fullscreen mode

Conclusion

These lesser-known JavaScript functions are like hidden treasures that can simplify your development tasks and make your code more elegant. By incorporating these functions into your projects, you can enhance your efficiency while making your code more readable and maintainable. Explore these features and use them wisely to unlock the full potential of JavaScript!

Top comments (3)

Collapse
 
lionelrowe profile image
lionel-rowe • Edited

Your example usage for getOwnPropertyDescriptors/defineProperty doesn't have any advantage over using { ...spread } or structuredClone. getOwnPropertyDescriptors/defineProperty are only necessary when you need to copy more complex properties, such as ones with getters and setters:

const alice = {
    name: 'Alice',
    age: 30,
    get isAdult() {
        return this.age >= 18
    },
}

const bob = structuredClone(alice)

const charlie = {}

for (const key in Object.getOwnPropertyDescriptors(alice)) {
    Object.defineProperty(charlie, key, descriptors[key])
}

bob.name = 'Bob'
bob.age = 5

charlie.name = 'Charlie'
charlie.age = 5

for (const { name, age, isAdult } of [alice, bob, charlie]) {
    console.log(`${age}-year-old ${name} is ${isAdult ? 'an adult' : 'a child'}`)
}

// output:
// 30-year-old Alice is an adult
// 5-year-old Bob is an adult
// 5-year-old Charlie is a child
Enter fullscreen mode Exit fullscreen mode

Also, you can combine text.trimStart().trimEnd() into text.trim() 😉

Collapse
 
josunlp profile image
Jonas Pfalzgraf

This isn't purely about advantages, it's more about nice to know features ;)

Collapse
 
lionelrowe profile image
lionel-rowe • Edited

I'm mainly responding to your claim "This can be particularly useful when you want to copy properties or clone objects without merely duplicating the values", which is wrong. getOwnPropertyDescriptors has nothing to do with copying vs cloning, and in any case, your example only deals with primitive-value properties (string, number), which have no distinction between copying or cloning.

const makePerson = ({ name, age, friends }) => ({
    name,
    age,
    friends: friends ?? [],
    get isAdult() {
        return this.age >= 18
    },
})

const alice = makePerson({ name: 'Alice', age: 30 })
const bob = makePerson({ name: 'Bob', age: 30 })
const charlie = bob
const devin = { ...bob }
const ellie = structuredClone(bob)
const frankie = {}
for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(bob))) {
    Object.defineProperty(frankie, key, descriptor)
}

charlie.friends.push(alice)

charlie.name = 'Charlie'
charlie.age = 5
devin.name = 'Devin'
devin.age = 5
ellie.name = 'Ellie'
ellie.age = 5
frankie.name = 'Frankie'
frankie.age = 5

console.log({ bob, charlie, devin, ellie, frankie })

// output:
{
    "bob": {
        "name": "Charlie",
        "age": 5,
        "friends": [alice],
        "isAdult": false
    },
    "charlie": {
        "name": "Charlie",
        "age": 5,
        "friends": [alice],
        "isAdult": false
    },
    "devin": {
        "name": "Devin",
        "age": 5,
        "friends": [alice],
        "isAdult": true
    },
    "ellie": {
        "name": "Ellie",
        "age": 5,
        "friends": [],
        "isAdult": true
    },
    "frankie": {
        "name": "Frankie",
        "age": 5,
        "friends": [alice],
        "isAdult": false
    }
}
Enter fullscreen mode Exit fullscreen mode

As you can see, using the spread operator (Devin) eliminates the reference problem (changing Charlie's name also changed Bob's), but only one level deep (adding to Charlie's friends list still affected Devin's). Using structuredClone (Ellie) eliminates that problem, but it doesn't properly handle computed properties, as you can see with the isAdult property. Finally, using getOwnPropertyDescriptors (Frankie) properly handles computed properties, but it doesn't handle deep cloning (alice still got added to Frankie's friends list, which probably wasn't what was intended).

it's more about nice to know features

It's only nice to know features if you know what they're good for and when to use them 😉