Rename articles prefixed with js-

This commit is contained in:
Angelos Chalaris
2023-05-18 23:29:52 +03:00
parent 2e31d65a39
commit 00f280bd16
63 changed files with 51 additions and 51 deletions

View File

@ -0,0 +1,27 @@
---
title: "Tip: Abort a fetch request in JavaScript"
shortTitle: Abort a fetch request
type: story
language: javascript
tags: [function]
author: chalarangelo
cover: cancel-typographer
excerpt: Aborting a fetch request in JavaScript is a common problem. Here's how to handle it correctly.
dateModified: 2022-05-15T05:00:00-04:00
---
The Fetch API is nowadays the de facto way to send asynchronous requests in JavaScript. This is in part due to the fact that the `fetch()` method accepts a multitude of useful options. One of these is the `signal` option, which can be used to abort a request. To create a valid value for this option, you can use `AbortController.signal` after creating a new instance of `AbortController`. Then, you can use `AbortController.abort()` to cancel the request at any time.
```js
// Create the AbortController
const controller = new AbortController();
const { signal } = controller;
// Perform the request
fetch('https://my.site.com/data', { signal }).then(res => console.log(res));
// Abort the request
controller.abort();
```
This is particularly useful in scenarios where a request takes too long or the response is no longer needed. You can see a common React use-case for this in the [useFetch hook](https://www.30secondsofcode.org/react/s/use-fetch).

View File

@ -0,0 +1,77 @@
---
title: Append elements to a JavaScript array
shortTitle: Append elements to array
type: story
language: javascript
tags: [array]
author: chalarangelo
cover: switzerland-night
excerpt: Have you ever tried appending elements to an array in JavaScript? Here's a primer on all the available options.
dateModified: 2022-07-10T05:00:00-04:00
---
Appending a value or values from an array in JavaScript is a pretty common task. While not hard to accomplish, there are a few approaches available, each with their own pros and cons. Choosing the correct one ultimately depends on the use case.
### Array.prototype.push()
The classical way to append elements to the end of an array is to use `Array.prototype.push()`. While versatile, you need to remember that it **mutates the original array**. On the flip side, it supports adding multiple elements at once. Finally, the return value of `Array.prototype.push()` is the new length of the array.
```js
const arr = ['a', 'b', 'c'];
arr.push('d', 'e'); // Returns 5 (new length after appending 2 elements)
// arr = ['a', 'b', 'c', 'd', 'e']
```
### Array.prototype.unshift()
Similar to `Array.prototype.push()`, `Array.prototype.unshift()` appends elements to the start of an array. Furthermore, this method also **mutates the original array** and supports adding multiple elements at once.
```js
const arr = ['a', 'b', 'c'];
arr.unshift('d', 'e'); // Returns 5 (new length after appending 2 elements)
// arr = ['d', 'e', 'a', 'b', 'c']
```
### Array.prototype.splice()
`Array.prototype.splice()` is more often used for removing elements from an array, but it can append elements, too. While it also **mutates the original array**, it can append elements anywhere in the array. Similar to previous methods, it supports adding more than one element at once. The return value of this method is practically meaningless in this use-case.
```js
const arr = ['a', 'b', 'c'];
arr.splice(1, 0, 'd', 'e');
// arr = ['a', 'd', 'e', 'b', 'c']
```
### Array.prototype.length
Appending an element to the end of an array means setting a value for an index equal to its length. This is due to arrays in JavaScript being zero-indexed. Luckily, `Array.prototype.length` can be combined with array index notation to accomplish this. Same as previous methods, this approach **mutates the original array**. Additionally, it's limited to adding one element at a time and only at the end of the array.
```js
const arr = ['a', 'b', 'c'];
arr[arr.length] = 'd';
// arr = ['a', 'b', 'c', 'd']
```
### Array.prototype.concat()
The first option that **doesn't mutate the original array** is `Array.prototype.concat()`. This method returns a new array with the elements of the original array and new elements concatenated to it. It can be used to append elements to either end of the array, while also supporting adding multiple elements at once.
```js
const arr = ['a', 'b', 'c'];
const arr2 = arr.concat('d', 'e');
// arr = ['a', 'b', 'c'], arr2 = ['a', 'b', 'c', 'd', 'e']
const arr3 = ['d', 'e'].concat(...arr);
// arr = ['a', 'b', 'c'], arr3 = ['d', 'e', 'a', 'b', 'c']
```
### Spread operator
Finally, the last option that **doesn't mutate the original array** is the spread operator (`...`). The way that it works is essentially the same as `Array.prototype.concat()`, supporting appending one or more elements to either end of the array.
```js
const arr = ['a', 'b', 'c'];
const arr2 = [...arr, 'd', 'e'];
// arr = ['a', 'b', 'c'], arr2 = ['a', 'b', 'c', 'd', 'e']
const arr3 = ['d', 'e', ...arr];
// arr = ['a', 'b', 'c'], arr3 = ['d', 'e', 'a', 'b', 'c']
```

View File

@ -0,0 +1,58 @@
---
title: JavaScript array filtering tips
shortTitle: Array filtering tips
type: story
language: javascript
tags: [array]
author: chalarangelo
cover: rocky-beach-waves
excerpt: A few tips and tricks to help you filter arrays in JavaScript more efficiently.
dateModified: 2022-09-28T05:00:00-04:00
---
While `Array.prototype.filter()` is a very convenient method, its performance often leaves something to be desired. This is exaggerated due to the fact that it has become the go-to method for many operations that can be performed using different alternatives. Let's look at a couple of common scenarios and see how we can improve their performance.
### Find a single value
If you are looking for a single result in an array, you can use `Array.prototype.find()` instead. This will return the first element that satisfies the condition, or `undefined` if no such element exists. It's much faster than `Array.prototype.filter()`, as it will stop iterating as soon as it finds the first matching element.
```js
const arr = [1, 2, 3, 4, 5];
arr.find(x => x > 3); // 4
```
Additionally, if the condition is a simple equality check, you can also use `Array.prototype.indexOf()`. While not as pretty as the other two, it can be significantly faster, as there's no overhead from using a comparator function.
```js
const arr = [1, 2, 3, 4, 5];
arr.indexOf(3); // 2
```
### Remove a single value
Similarly, if you want to remove a single value from an array, you can use `Array.prototype.findIndex()` to find the index of the element you want to remove. Then, use `Array.prototype.slice()` to remove it. While this is a little more verbose and seems to perform more operations, it can actually be faster than using `Array.prototype.filter()` in many cases.
```js
const arr = [1, 2, 3, 4, 5];
const index = arr.findIndex(x => x === 3);
const newArr = [...arr.slice(0, index), ...arr.slice(index + 1)];
// [1, 2, 4, 5]
```
Similarly, if you don't mind mutating the original array, you can use `Array.prototype.splice()` to remove the element at the index you found. As this method doesn't have to create a new array, it can be significantly faster than the previous one.
```js
const arr = [1, 2, 3, 4, 5];
const index = arr.findIndex(x => x === 3);
arr.splice(index, 1); // [1, 2, 4, 5]
```
### Additional notes
In many cases, such changes will not make a drastic difference in your application's performance. It never hurts, however, to be aware of all the options and use the best one for your specific use case. Changes such as these will make more sense when working with large datasets, as well as critical parts of your application.
Additionally, depending on the data and its constraints, it might make more sense to use a different data structure altogether. For example, if unique values are a precondition, using a `Set` is more efficient and much easier to work with in many cases.

View File

@ -0,0 +1,75 @@
---
title: The many ways to initialize a JavaScript Array
shortTitle: Array Initialization
type: story
language: javascript
tags: [array]
author: chalarangelo
cover: red-mountain-range
excerpt: Discover the inner workings of JavaScript arrays and learn about the different ways to initialize them.
dateModified: 2023-06-18T05:00:00-04:00
---
Initializing arrays in JavaScript is a crucial task, with many techniques to choose from and performance considerations to keep in mind. While there might not be a one-size-fits-all solution, there are a few options you might want to consider.
### Array() constructor
The first thing you'd reach for would probably be the `Array()` constructor. Counterintuitively, this is probably the most problematic option to use on its own. While it works for any number of arguments to create an array with the given values, it falls short pretty much everywhere else. Most of its problems stem from **holes or "empty" values** with which the resulting array is populated and how these are handled elsewhere.
```js
const arr = Array(3); // [ , , ] - 3 empty slots
arr.map(() => 1); // [ , , ] - map() skips empty slots
arr.map((_, i) => i); // [ , , ] - map() skips empty slots
arr[0]; // undefined - actually, it is an empty slot
```
### Array.from()
`Array.from()` is a static method that creates a new, shallow-copied Array instance from an **array-like or iterable object**. It is very useful for converting array-like objects (e.g. `arguments`, `NodeList`) or iterables (e.g. `Set`, `Map`, `Generator`) into actual arrays. Apart from that, it can easily be "tricked" into creating an array of a given length by passing an object with a `length` property. This is somewhat slow, but it works well and circumvents some of the problems of the `Array()` constructor. Additionally, it allows you to pass a **mapping function** as a second argument, which is very useful for initializing arrays with values.
```js
const arr = Array.from({ length: 3 }); // [undefined, undefined, undefined]
arr.map(() => 1); // [1, 1, 1]
arr.map((_, i) => i); // [0, 1, 2]
const staticArr = Array.from({ length: 3 }, () => 1); // [1, 1, 1]
const indexArr = Array.from({ length: 3 }, (_, i) => i); // [0, 1, 2]
```
### Array.prototype.fill()
While `Array.from()` is quite flexible, using a mapping function to fill it with the same value isn't particularly efficient. `Array.prototype.fill()` comes to fill this gap by allowing you to **fill an existing array** with the same value. This can also come in handy in conjunction with the `Array()` constructor, as it allows you to fill the array with a value, instead of empty slots.
```js
const nullArr = new Array(3).fill(null); // [null, null, null]
const staticArr = Array.from({ length: 3 }).fill(1); // [1, 1, 1]
const indexArr = Array(3).fill(null).map((_, i) => i); // [0, 1, 2]
```
### Array.prototype.map()
`Array.from()` allows for a mapping function via a second argument, but a lot of people think it's hard to read. Additionally, there are a few edge cases, where having access to the array itself during mapping can be useful. `Array.prototype.map()` gives you this little bit of extra flexbility and readability if that's what you're concerned about. It's also able to do pretty much everything else you might need, but remember that it **doesn't work well with empty values**.
```js
const arr = Array(3).map(() => 1); // [ , , ] - map() skips empty slots
const staticArr = Array.from({ length: 3 }).map(() => 1); // [1, 1, 1]
const indexArr = Array.from({ length: 3 }).map((_, i) => i); // [0, 1, 2]
const fractionArr =
Array.from({ length: 3 }).map((_, i, a) => i / a.length); // [0, 0.5, 1]
```
### A note on performance
Performance might be a concern if this sort of operation is very common in your application, but overall none of these options are particularly slow. The `Array()` constructor seems to be the fastest. That being said, if combined with `Array.prototype.fill()`, it can be the best option for initializing an array with a single value. Oddly enough, this performance advantage still holds even if you chain an `Array.prototype.map()` call afterwards to create dynamic values. Therefore, my personal recommendations are as follows:
```js
const initializeArrayWithValues = (n, val = 0) => Array(n).fill(val);
const initializeMappedArray = (n, mapFn = (_, i) => i) =>
Array(n).fill(null).map(mapFn);
```
```js
initializeArrayWithValues(4, 2); // [2, 2, 2, 2]
initializeMappedArray(4, (_, i) => i * 2); // [0, 2, 4, 6]
```
You can learn more tips and tricks related to JavaScript array initialization in [this collection](//js/array-initialization/p/1).

View File

@ -0,0 +1,31 @@
---
title: What is the difference between Array.prototype.map() and Array.prototype.forEach()?
shortTitle: Array.prototype.map() vs Array.prototype.forEach()
type: question
language: javascript
tags: [array]
author: chalarangelo
cover: fort-lamp
excerpt: Which method do you reach for first? What are the differences between them? Let's find out!
dateModified: 2023-03-26T05:00:00-04:00
---
`Array.prototype.map()` and `Array.prototype.forEach()` are two of the most commonly used methods in JavaScript. Both of them iterate over an array and perform some action on each element. Yet, they're not the same and they're not interchangeable.
The key difference between the two lies in the **return value**. The return value of `Array.prototype.map()` is a new array with the results of the callback function. On the other hand, the return value of `Array.prototype.forEach()` is `undefined`.
Simply put, `Array.prototype.forEach()` is used to perform some action on each element in an array, while `Array.prototype.map()` is used to create a new array based on the elements in the original array. Let's take a look at an example to clear up any confusion:
```js
const numbers = [1, 2, 3, 4, 5];
numbers.forEach(num => console.log(num * 2));
// No return value, output: 2, 4, 6, 8, 10
const doubledNumbers = numbers.map(num => num * 2);
// Returns a new array: [2, 4, 6, 8, 10]
```
The way I like to distinguish them is by remembering that `Array.prototype.map()` represents a **transformation**, whereas `Array.prototype.forEach()` represents an **iteration**. Hopefully, one of these explanations will click for you and help you remember the difference between the two.
As a closing note, I would like to remind you that the humble `for` loop can be more efficient in some cases, such as [breaking out of a loop early](/js/s/for-loop-early-break). Always pick the right tool for the job, as ES6 has a method for almost every use case.

View File

@ -0,0 +1,22 @@
---
title: "Tip: Min and max value in a JavaScript array"
shortTitle: Min and max value of an array
type: tip
language: javascript
tags: [array,math]
author: chalarangelo
cover: little-tree
excerpt: When working with numeric arrays in JavaScript, you might need to find the minimum or maximum value. Here's a quick and easy way to do it.
dateModified: 2021-11-06T20:51:47+03:00
---
When working with numeric arrays in JavaScript, you might find yourself in need of finding the minimum or maximum value. Luckily, JavaScript's `Math` built-in object has got you covered. You can simply use `Math.min()` or `Math.max()` combined with the spread operator (`...`), as both functions accept any number of arguments.
```js
const nums = [2, 4, 6, 8, 1, 3, 5, 7];
Math.max(...nums); // 8
Math.min(...nums); // 1
```
For more complex cases, such as finding the min/max value in an array of objects, you might have to resort to `Array.prototype.map()` or `Array.prototype.reduce()`. On the other hand, our [minBy](/js/s/min-by) or [maxBy](/js/s/max-by) snippets might be all you need.

View File

@ -0,0 +1,42 @@
---
title: "Tip: JavaScript array sorting shorthand"
shortTitle: Array sorting shorthand
type: tip
language: javascript
tags: [array]
cover: apples
excerpt: Learn how to quickly write code to sort JavaScript arrays with this handy one-liner.
dateModified: 2021-06-12T19:30:41+03:00
---
When sorting an array of primitive values (e.g. strings or numbers), you'll often see a lot of code that looks like this:
```js
const arr = [8, 2, 1, 4, 5, 0];
// Sort in ascending order
arr.sort((a, b) => {
if (a > b) return 1;
if (b > a) return -1
return 0;
}); // [0, 1, 2, 4, 5, 8]
```
While this piece of code does the job, there is also a one-line alternative for it. The trick hinges on `Array.prototype.sort()` expecting either a positive or a negative value to perform a swap between two elements, thus allowing for more flexible values than `1` and `-1`. Subtracting the numeric values in an array is sufficient and can also be used to sort the array the other way around:
```js
const arr = [8, 2, 1, 4, 5, 0];
// Sort in ascending order
arr.sort((a, b) => a - b); // [0, 1, 2, 4, 5, 8]
// Sort in descending order
arr.sort((a, b) => b - a); // [8, 5, 4, 2, 1, 0]
```
If you are working with string arrays, you should instead use `String.prototype.localeCompare()`, as it provides far greater flexibility, by accounting for specific locales and their unique needs:
```js
const s = ['Hi', 'Hola', 'Hello'];
// Sort in ascending order
arr.sort((a, b) => a.localeCompare(b)); // ['Hello', 'Hi', 'Hola']
// Sort in descending order
arr.sort((a, b) => b.localeCompare(a)); // ['Hola', 'Hi', 'Hello']
```

View File

@ -0,0 +1,43 @@
---
title: What is the difference between async and defer in script loading?
shortTitle: Async and defer
type: question
language: javascript
tags: [html]
author: chalarangelo
cover: coworking-space
excerpt: Understanding how to correctly load your JavaScript files can significantly improve your web application's performance.
dateModified: 2022-09-04T05:00:00-04:00
---
When it comes to loading JavaScript files, there are a few different options available. Understanding exactly how scripts are loaded and executed is crucial for website performance, as well as for the overall quality of the user experience. Let's take a look at how the `<script>` tag works and how certain attributes affect its behavior.
![Script loading visualization](./illustrations/async-defer.png)
### No attributes
More often than not, a plain `<script>` tag without attributes is what most people tend to start with. This implements the default browser behavior. The HTML will be parsed until the script tag is encountered. At this point, HTML parsing will be paused and the script will be loaded. The script will then be executed before HTML parsing can resume.
```html
<script src="script.js"></script>
```
As you can see, this method can cause a long pause in HTML parsing, resulting in a degraded user experience.
### The async attribute
To avoid a long pause in HTML parsing, the `async` attribute can be leveraged. This ensures that, when the script is encountered, parsing doesn't pause right away. Instead, the script is loaded in the background and only the HTML parsing is paused to execute it. HTML parsing resumes as usual after script execution is completed.
```html
<script src="script.js" async></script>
```
While the `async` attribute takes steps to mitigate the issue mentioned previously, it comes with an important caveat. Scripts loaded this way are not guaranteed to execute in the order specified, but rather as they become available when they are loaded.
### The defer attribute
Finally, the `defer` attribute builds on top of the previous behavior to guarantee order of execution for scripts. As previously, scripts are loaded in the background as they are encountered. When the HTML parsing finishes, they are then executed in order.
```html
<script src="script.js" defer></script>
```

View File

@ -0,0 +1,35 @@
---
title: What is a callback function?
shortTitle: Callback functions
type: question
language: javascript
tags: [function]
author: chalarangelo
cover: rabbit-call
excerpt: JavaScript uses callback functions quite a lot. From event listeners to asynchronous code, they're an invaluable tool you need to master.
dateModified: 2021-10-03T05:00:00-04:00
---
A callback function is a function passed as an argument to another function, which is then invoked inside the outer function. Callback functions are often executed once an event has occurred or a task has completed.
### Synchronous callbacks
A synchronous callback is a callback function that is executed immediately. The function passed as the first argument to `Array.prototype.map()` is a great example of a synchronous callback:
```js
const nums = [1, 2, 3];
const printDoublePlusOne = n => console.log(2 * n + 1);
nums.map(printDoublePlusOne); // LOGS: 3, 5, 7
```
### Asynchronous callbacks
An asynchronous callback is a callback function that is used to execute code after an asynchronous operation has completed. The function executed inside `Promise.prototype.then()` is a great example of an asynchronous callback:
```js
const nums = fetch('https://api.nums.org'); // Suppose the response is [1, 2, 3]
const printDoublePlusOne = n => console.log(2 * n + 1);
nums.then(printDoublePlusOne); // LOGS: 3, 5, 7
```

View File

@ -0,0 +1,33 @@
---
title: "Tip: You can't extend the Proxy object"
shortTitle: Extending the Proxy object
type: tip
language: javascript
tags: [object,proxy]
author: chalarangelo
cover: icebreaker
excerpt: Turns out the Proxy object is not extensible, but there's a way around its limitations.
dateModified: 2023-04-17T05:00:00-04:00
---
While the [`Proxy`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) object seems like it can be extended by any other class in JavaScript, that's not the case. This is due to proxy objects having very atypical semantics and being considered **exotic objects**. Simply put, this means they do not have a prototype and are not extensible.
So how do you extend a proxy object? You don't. You can, however, create a class that returns a proxy by returning it from the constructor. After all, this is probably the sort of behavior you're after.
```js
class MyProxy {
constructor(value) {
Object.keys(value).forEach(key => (this[key] = value[key]));
return new Proxy(this, {
set(object, key, value) {
console.log(`Called with ${key} = ${value}`);
object[key] = value;
return true;
}
});
}
}
const myProxy = new MyProxy({ a: 1 });
myProxy.b = 2; // LOGS: 'Called with b = 2'
```

View File

@ -0,0 +1,27 @@
---
title: "Tip: Compare strings regardless of case and accent"
shortTitle: Case and accent-insensitive string comparison
type: tip
language: javascript
tags: [string,comparison]
author: chalarangelo
cover: memories-of-pineapple-1
excerpt: Here's a quick tip on how to compare and sort arrays of strings, ignoring case and accents.
dateModified: 2022-07-17T05:00:00-04:00
---
Comparing and sorting JavaScript strings is rather common. Usually, the use of `String.prototype.localeCompare()` is sufficient for sorting arrays of strings in most scenarios. Dealing with accents and cases can get tricky, however, and lead to unexpected results. This is where `Intl.Collator` comes into play, an object used for language-sensitive string comparison. Using `Intl.Collator.prototype.compare()`, you can sort strings regardless of their case or accent and it can even accept a locale argument.
```js
const arr = ['ä', 'a', 'b', 'A', 'B', 'Å'];
const localeCompare = (a, b) => a.localeCompare(b);
const collator = new Intl.Collator();
const deCollator = new Intl.Collator('de');
const svCollator = new Intl.Collator('sv');
arr.sort(localeCompare); // ['a', 'A', 'Å', 'ä', 'b', 'B']
arr.sort(collator.compare); // ['a', 'A', 'Å', 'ä', 'b', 'B']
arr.sort(deCollator.compare); // ['a', 'A', 'Å', 'ä', 'b', 'B']
arr.sort(svCollator.compare); // ['a', 'A', 'b', 'B', 'Å', 'ä']
```

View File

@ -0,0 +1,66 @@
---
title: How to construct a URL in JavaScript
shortTitle: Construct a URL in JavaScript
type: story
language: javascript
tags: [string,browser]
author: chalarangelo
cover: alfama
excerpt: A short guide on how to correctly construct a URL in JavaScript.
dateModified: 2023-02-26T05:00:00-04:00
---
Oftentimes, we need to **create a URL in JavaScript**, to request a resource or redirect the user. A seemingly simple task, yet URLs can be quite nuanced and complex, requiring a lot of attention to get right. This rings especially true if you've ever worked with different encodings and multiple **query parameters**.
Naively, many developers reach for template literals to construct a URL. After all, URLs are simply strings and interpolation can be used to add parameters as needed. Except, this approach is error-prone and can lead to bugs. Let's take a look at an example:
```js
const query = "Where's Waldø?";
const locale = "en-US";
const campaign = "promo_email";
const url = `https://examp.le?q=${query}&lang=${locale}&from=${campaign}`;
// "https://examp.le?q=Where's Waldø?&lang=en-US&from=promo_email"
```
As you can see, template literals aren't well-suited for URLs, as they won't **encode special characters**. If you've worked with JavaScript for any amount of time, you might reach for `encodeURIComponent()` to fix this:
```js
const query = "Where's Waldø?";
const locale = "en-US";
const campaign = "promo_email";
const url = `https://examp.le
?q=${
encodeURIComponent(query)
}&lang=${
encodeURIComponent(locale)
}&from=${
encodeURIComponent(campaign)
}`;
// "https://examp.le\n ?q=Where's%20Wald%C3%B8%3F&lang=en-US&from=promo_email"
```
Surely, this dealt with encoding, but the multiline string has sneaked a newline character (`\n`) into the URL. This is because template literals preserve whitespace, leading to such issues. Breaking the string into multiple lines and concatenating them can help, but the code is starting to get ugly.
Obviously, there are other issues that might come into play, making this a **non-trivial problem** to solve. Luckily, there's a better way to construct URLs in JavaScript, using the `URL` object. Let's see how we can use it to solve the problem above:
```js
const query = "Where's Waldø?";
const locale = "en-US";
const campaign = "promo_email";
// Good
const url = new URL('https://examp.le');
url.searchParams.set('q', query);
url.searchParams.set('lang', locale);
url.searchParams.set('from', campaign);
url.toString();
// 'https://examp.le/?q=Where%27s+Wald%C3%B8%3F&lang=en-US&from=promo_email'
```
Admittedly, using the `URL` object can be a bit more verbose, but it's a worthwhile tradeoff. The code is much more readable and maintainable, and it's less prone to errors. Query parameters are now encoded properly, and delimiters are added automatically.
While query parameters are the most common issue when dealing with URLs, the `URL` object can be useful in many other situations. For example, you can change the protocol, hostname, port, path, hash, etc. of a URL, or even parse an existing URL into its components. If you're interested, you should check out previous articles about how you can [edit URL parameters](/js/s/edit-url-params) and the [`window.location` cheat sheet](/js/s/window-location-cheatsheet).

View File

@ -0,0 +1,59 @@
---
title: Creating HTML elements in JavaScript
shortTitle: Creating HTML elements
type: story
language: javascript
tags: [browser]
author: chalarangelo
cover: body-of-water
excerpt: Learn how to create HTML elements in JavaScript, by abstracting the creation logic into a function.
dateModified: 2022-05-29T05:00:00-04:00
---
JavaScript's `Document.createElement()` method is used to create new HTML elements. Here's what that looks like in action:
```js
const root = document.body;
const newElement = document.createElement('div');
newElement.textContent = 'Hello World';
root.append(newElement);
```
As you can see, creating an element is easy. The tiresome part is having to set all of its attributes and then add it to the DOM. Worse even, creating multiple elements with the same set of attributes requires a lot of repetitive code.
Luckily, we can abstract element creation into a function. In fact, we can use objects to pass attributes to the element. Using `Object.entries()` we can iterate over the object and set the attributes. Here's what that looks like:
```js
const root = document.body;
const createElement = (el, parent, prepend = false) => {
const { nodeName = 'div', ...attrs } = el;
const element = document.createElement(nodeName);
Object.entries(attrs).forEach(([attr, value]) => {
element[attr] = value;
});
if (prepend) parent.prepend(element);
else parent.append(element);
};
createElement(
{
nodeName: 'div',
textContent: 'Hello world',
},
root
);
createElement(
{
nodeName: 'p',
textContent: 'Hi',
},
root,
true
);
```
That's pretty useful, but what happens if we have an HTML string we want to create an element from, instead? We have a [createElement snippet](/js/s/create-element) that does something along those lines. The only part that's missing is appending it to the parent element.

View File

@ -0,0 +1,218 @@
---
title: JavaScript Data Structures - Binary Search Tree
shortTitle: Binary Search Tree
type: story
language: javascript
tags: [class]
author: chalarangelo
cover: purple-flower-macro-4
excerpt: A binary search tree is a hierarchical data structure of ordered nodes with at most two children each.
dateModified: 2021-08-31T05:00:00-04:00
---
### Definition
A binary search tree is a data structure consisting of a set of ordered linked nodes that represent a hierarchical tree structure. Each node is linked to others via parent-children relationship. Any given node can have at most two children (left and right). The first node in the binary search tree is the root, whereas nodes without any children are the leaves. The binary search tree is organized in such a way that for any given node, all nodes in its left subtree have a key less than itself and all nodes in its right subtree have a key greater than itself.
![JavaScript Binary Search Tree visualization](./illustrations/ds-binary-search-tree.png)
Each node in a binary search tree data structure must have the following properties:
- `key`: The key of the node
- `value`: The value of the node
- `parent`: The parent of the node (`null` if there is none)
- `left`: A pointer to the node's left child (`null` if there is none)
- `right`: A pointer to the node's right child (`null` if there is none)
The main operations of a binary search tree data structure are:
- `insert`: Inserts a node as a child of the given parent node
- `remove`: Removes a node and its children from the binary search tree
- `has`: Checks if a given node exists
- `find`: Retrieves a given node
- `preOrderTraversal`: Traverses the binary search tree by recursively traversing each node followed by its children
- `postOrderTraversal`: Traverses the binary search tree by recursively traversing each node's children followed by the node
- `inOrderTraversal`: Traverses the binary search tree by recursively traversing each node's left child, followed by the node, followed by its right child
### Implementation
```js
class BinarySearchTreeNode {
constructor(key, value = key, parent = null) {
this.key = key;
this.value = value;
this.parent = parent;
this.left = null;
this.right = null;
}
get isLeaf() {
return this.left === null && this.right === null;
}
get hasChildren() {
return !this.isLeaf;
}
}
class BinarySearchTree {
constructor(key, value = key) {
this.root = new BinarySearchTreeNode(key, value);
}
*inOrderTraversal(node = this.root) {
if (node.left) yield* this.inOrderTraversal(node.left);
yield node;
if (node.right) yield* this.inOrderTraversal(node.right);
}
*postOrderTraversal(node = this.root) {
if (node.left) yield* this.postOrderTraversal(node.left);
if (node.right) yield* this.postOrderTraversal(node.right);
yield node;
}
*preOrderTraversal(node = this.root) {
yield node;
if (node.left) yield* this.preOrderTraversal(node.left);
if (node.right) yield* this.preOrderTraversal(node.right);
}
insert(key, value = key) {
let node = this.root;
while (true) {
if (node.key === key) return false;
if (node.key > key) {
if (node.left !== null) node = node.left;
else {
node.left = new BinarySearchTreeNode(key, value, node);
return true;
}
} else if (node.key < key) {
if (node.right !== null) node = node.right;
else {
node.right = new BinarySearchTreeNode(key, value, node);
return true;
}
}
}
}
has(key) {
for (let node of this.postOrderTraversal()) {
if (node.key === key) return true;
}
return false;
}
find(key) {
for (let node of this.postOrderTraversal()) {
if (node.key === key) return node;
}
return undefined;
}
remove(key) {
const node = this.find(key);
if (!node) return false;
const isRoot = node.parent === null;
const isLeftChild = !isRoot ? node.parent.left === node : false;
const hasBothChildren = node.left !== null && node.right !== null;
if (node.isLeaf) {
if (!isRoot) {
if (isLeftChild) node.parent.left = null;
else node.parent.right = null;
} else {
this.root = null;
}
return true;
} else if (!hasBothChildren) {
const child = node.left !== null ? node.left : node.right;
if (!isRoot) {
if (isLeftChild) node.parent.left = child;
else node.parent.right = child;
} else {
this.root = child;
}
child.parent = node.parent;
return true;
} else {
const rightmostLeft = [...this.inOrderTraversal(node.left)].slice(-1)[0];
rightmostLeft.parent = node.parent;
if (!isRoot) {
if (isLeftChild) node.parent.left = rightmostLeft;
else node.parent.right = rightmostLeft;
} else {
this.root = rightmostLeft;
}
rightmostLeft.right = node.right;
node.right.parent = rightmostLeft;
return true;
}
}
}
```
- Create a `class` for the `BinarySearchTreeNode` with a `constructor` that initializes the appropriate `key`, `value`, `parent`, `left` and `right` properties.
- Define an `isLeaf` getter, that uses `Array.prototype.length` to check if both `left` and `right` are empty.
- Define a `hasChildren` getter, that is the reverse of the `isLeaf` getter.
- Create a `class` for the `BinarySearchTree` with a `constructor` that initializes the `root` of the binary search tree.
- Define a `preOrderTraversal()` generator method that traverses the binary search tree in pre-order, using the `yield*` syntax to recursively delegate traversal to itself.
- Define a `postOrderTraversal()` generator method that traverses the binary search tree in post-order, using the `yield*` syntax to recursively delegate traversal to itself.
- Define a `inOrderTraversal()` generator method that traverses the binary search tree in in-order, using the `yield*` syntax to recursively delegate traversal to itself.
- Define an `insert()` method, that uses a `while` loop to search the binary search tree, moving through each node's children, until an appropriate position is found to insert a new child `BinarySearchTreeNode` either as the `left` or `right` child, depending on the given `key`.
- Define a `has()` method, that uses the `preOrderTraversal()` method to check if the given node exists in the binary search tree.
- Define a `find()` method, that uses the `preOrderTraversal()` method to retrieve the given node in the binary search tree.
- Define a `remove()` method, that removes the given `BinarySearchTreeNode` from the binary search tree, deleting any links to it and updating the binary search tree to retain its order.
```js
const tree = new BinarySearchTree(30);
tree.insert(10);
tree.insert(15);
tree.insert(12);
tree.insert(40);
tree.insert(35);
tree.insert(50);
[...tree.preOrderTraversal()].map(x => x.value);
// [30, 10, 15, 12, 40, 35, 50]
[...tree.inOrderTraversal()].map(x => x.value);
// [10, 12, 15, 30, 35, 40, 50]
[...tree.postOrderTraversal()].map(x => x.value);
// [12, 15, 10, 35, 50, 40, 30]
tree.root.value; // 30
tree.root.hasChildren; // true
tree.find(12).isLeaf; // true
tree.find(40).isLeaf; // false
tree.find(50).parent.value; // 40
tree.find(15).left.value; // 12
tree.find(12).right; // null
tree.remove(12);
[...tree.preOrderTraversal()].map(x => x.value);
// [30, 10, 15, 40, 35, 50]
tree.remove(10);
[...tree.preOrderTraversal()].map(v => ({
key: v.key,
parent: v.parent ? v.parent.key : null,
})); // [30, 15, 40, 35, 50]
tree.remove(40);
[...tree.preOrderTraversal()].map(x => x.value);
// [30, 15, 40, 35, 50]
tree.remove(30);
[...tree.preOrderTraversal()].map(x => x.value);
// [15, 35, 50]
```

View File

@ -0,0 +1,164 @@
---
title: JavaScript Data Structures - Binary Tree
shortTitle: Binary Tree
type: story
language: javascript
tags: [class]
author: chalarangelo
cover: purple-flower-macro-3
excerpt: A binary tree is a hierarchical data structure of linked nodes with at most two children each.
dateModified: 2021-08-26T05:00:00-04:00
---
### Definition
A binary tree is a data structure consisting of a set of linked nodes that represent a hierarchical tree structure. Each node is linked to others via parent-children relationship. Any given node can have at most two children (left and right). The first node in the binary tree is the root, whereas nodes without any children are the leaves.
![JavaScript Binary Tree visualization](./illustrations/ds-binary-tree.png)
Each node in a binary tree data structure must have the following properties:
- `key`: The key of the node
- `value`: The value of the node
- `parent`: The parent of the node (`null` if there is none)
- `left`: A pointer to the node's left child (`null` if there is none)
- `right`: A pointer to the node's right child (`null` if there is none)
The main operations of a binary tree data structure are:
- `insert`: Inserts a node as a child of the given parent node
- `remove`: Removes a node and its children from the binary tree
- `find`: Retrieves a given node
- `preOrderTraversal`: Traverses the binary tree by recursively traversing each node followed by its children
- `postOrderTraversal`: Traverses the binary tree by recursively traversing each node's children followed by the node
- `inOrderTraversal`: Traverses the binary tree by recursively traversing each node's left child, followed by the node, followed by its right child
### Implementation
```js
class BinaryTreeNode {
constructor(key, value = key, parent = null) {
this.key = key;
this.value = value;
this.parent = parent;
this.left = null;
this.right = null;
}
get isLeaf() {
return this.left === null && this.right === null;
}
get hasChildren() {
return !this.isLeaf;
}
}
class BinaryTree {
constructor(key, value = key) {
this.root = new BinaryTreeNode(key, value);
}
*inOrderTraversal(node = this.root) {
if (node.left) yield* this.inOrderTraversal(node.left);
yield node;
if (node.right) yield* this.inOrderTraversal(node.right);
}
*postOrderTraversal(node = this.root) {
if (node.left) yield* this.postOrderTraversal(node.left);
if (node.right) yield* this.postOrderTraversal(node.right);
yield node;
}
*preOrderTraversal(node = this.root) {
yield node;
if (node.left) yield* this.preOrderTraversal(node.left);
if (node.right) yield* this.preOrderTraversal(node.right);
}
insert(
parentNodeKey,
key,
value = key,
{ left, right } = { left: true, right: true }
) {
for (let node of this.preOrderTraversal()) {
if (node.key === parentNodeKey) {
const canInsertLeft = left && node.left === null;
const canInsertRight = right && node.right === null;
if (!canInsertLeft && !canInsertRight) return false;
if (canInsertLeft) {
node.left = new BinaryTreeNode(key, value, node);
return true;
}
if (canInsertRight) {
node.right = new BinaryTreeNode(key, value, node);
return true;
}
}
}
return false;
}
remove(key) {
for (let node of this.preOrderTraversal()) {
if (node.left.key === key) {
node.left = null;
return true;
}
if (node.right.key === key) {
node.right = null;
return true;
}
}
return false;
}
find(key) {
for (let node of this.preOrderTraversal()) {
if (node.key === key) return node;
}
return undefined;
}
}
```
- Create a `class` for the `BinaryTreeNode` with a `constructor` that initializes the appropriate `key`, `value`, `parent`, `left` and `right` properties.
- Define an `isLeaf` getter, that uses `Array.prototype.length` to check if both `left` and `right` are empty.
- Define a `hasChildren` getter, that is the reverse of the `isLeaf` getter.
- Create a `class` for the `BinaryTree` with a `constructor` that initializes the `root` of the binary tree.
- Define a `preOrderTraversal()` generator method that traverses the binary tree in pre-order, using the `yield*` syntax to recursively delegate traversal to itself.
- Define a `postOrderTraversal()` generator method that traverses the binary tree in post-order, using the `yield*` syntax to recursively delegate traversal to itself.
- Define a `inOrderTraversal()` generator method that traverses the binary tree in in-order, using the `yield*` syntax to recursively delegate traversal to itself.
- Define an `insert()` method, that uses the `preOrderTraversal()` method to find the given parent node and insert a new child `BinaryTreeNode` either as the `left` or `right` child, depending on the passed options object.
- Define a `remove()` method, that uses the `preOrderTraversal()` method and `Array.prototype.filter()` to remove a `BinaryTreeNode` from the binary tree.
- Define a `find()` method, that uses the `preOrderTraversal()` method to retrieve the given node in the binary tree.
```js
const tree = new BinaryTree(1, 'AB');
tree.insert(1, 11, 'AC');
tree.insert(1, 12, 'BC');
tree.insert(12, 121, 'BG', { right: true });
[...tree.preOrderTraversal()].map(x => x.value);
// ['AB', 'AC', 'BC', 'BCG']
[...tree.inOrderTraversal()].map(x => x.value);
// ['AC', 'AB', 'BC', 'BG']
tree.root.value; // 'AB'
tree.root.hasChildren; // true
tree.find(12).isLeaf; // false
tree.find(121).isLeaf; // true
tree.find(121).parent.value; // 'BC'
tree.find(12).left; // null
tree.find(12).right.value; // 'BG'
tree.remove(12);
[...tree.postOrderTraversal()].map(x => x.value);
// ['AC', 'AB']
```

View File

@ -0,0 +1,148 @@
---
title: JavaScript Data Structures - Doubly Linked List
shortTitle: Doubly Linked List
type: story
language: javascript
tags: [class]
author: chalarangelo
cover: purple-flower-macro-4
excerpt: A doubly linked list is a linear data structure where each element points both to the next and the previous one.
dateModified: 2021-08-12T05:00:00-04:00
---
### Definition
A doubly linked list is a linear data structure that represents a collection of elements, where each element points both to the next and the previous one. The first element in the doubly linked list is the head and the last element is the tail.
![JavaScript Doubly Linked List visualization](./illustrations/ds-doubly-linked-list.png)
Each element of a doubly linked list data structure must have the following properties:
- `value`: The value of the element
- `next`: A pointer to the next element in the linked list (`null` if there is none)
- `previous`: A pointer to the previous element in the linked list (`null` if there is none)
The main properties of a doubly linked list data structure are:
- `size`: The number of elements in the doubly linked list
- `head`: The first element in the doubly linked list
- `tail`: The last element in the doubly linked list
The main operations of a doubly linked list data structure are:
- `insertAt`: Inserts an element at the specific index
- `removeAt`: Removes the element at the specific index
- `getAt`: Retrieves the element at the specific index
- `clear`: Empties the doubly linked list
- `reverse`: Reverses the order of elements in the doubly linked list
### Implementation
```js
class DoublyLinkedList {
constructor() {
this.nodes = [];
}
get size() {
return this.nodes.length;
}
get head() {
return this.size ? this.nodes[0] : null;
}
get tail() {
return this.size ? this.nodes[this.size - 1] : null;
}
insertAt(index, value) {
const previousNode = this.nodes[index - 1] || null;
const nextNode = this.nodes[index] || null;
const node = { value, next: nextNode, previous: previousNode };
if (previousNode) previousNode.next = node;
if (nextNode) nextNode.previous = node;
this.nodes.splice(index, 0, node);
}
insertFirst(value) {
this.insertAt(0, value);
}
insertLast(value) {
this.insertAt(this.size, value);
}
getAt(index) {
return this.nodes[index];
}
removeAt(index) {
const previousNode = this.nodes[index - 1] || null;
const nextNode = this.nodes[index + 1] || null;
if (previousNode) previousNode.next = nextNode;
if (nextNode) nextNode.previous = previousNode;
return this.nodes.splice(index, 1);
}
clear() {
this.nodes = [];
}
reverse() {
this.nodes = this.nodes.reduce((acc, { value }) => {
const nextNode = acc[0] || null;
const node = { value, next: nextNode, previous: null };
if (nextNode) nextNode.previous = node;
return [node, ...acc];
}, []);
}
*[Symbol.iterator]() {
yield* this.nodes;
}
}
```
- Create a `class` with a `constructor` that initializes an empty array, `nodes`, for each instance.
- Define a `size` getter, that returns that uses `Array.prototype.length` to return the number of elements in the `nodes` array.
- Define a `head` getter, that returns the first element of the `nodes` array or `null` if empty.
- Define a `tail` getter, that returns the last element of the `nodes` array or `null` if empty.
- Define an `insertAt()` method, which uses `Array.prototype.splice()` to add a new object in the `nodes` array, updating the `next` and `previous` keys of the previous and next elements respectively.
- Define two convenience methods, `insertFirst()` and `insertLast()` that use the `insertAt()` method to insert a new element at the start or end of the `nodes` array respectively.
- Define a `getAt()` method, which retrieves the element in the given `index`.
- Define a `removeAt()` method, which uses `Array.prototype.splice()` to remove an object in the `nodes` array, updating the `next` and `previous` keys of the previous and next elements respectively.
- Define a `clear()` method, which empties the `nodes` array.
- Define a `reverse()` method, which uses `Array.prototype.reduce()` and the spread operator (`...`) to reverse the order of the `nodes` array, updating the `next` and `previous` keys of each element appropriately.
- Define a generator method for `Symbol.iterator`, which delegates to the `nodes` array's iterator using the `yield*` syntax.
```js
const list = new DoublyLinkedList();
list.insertFirst(1);
list.insertFirst(2);
list.insertFirst(3);
list.insertLast(4);
list.insertAt(3, 5);
list.size; // 5
list.head.value; // 3
list.head.next.value; // 2
list.tail.value; // 4
list.tail.previous.value; // 5
[...list.map(e => e.value)]; // [3, 2, 1, 5, 4]
list.removeAt(1); // 2
list.getAt(1).value; // 1
list.head.next.value; // 1
[...list.map(e => e.value)]; // [3, 1, 5, 4]
list.reverse();
[...list.map(e => e.value)]; // [4, 5, 1, 3]
list.clear();
list.size; // 0
```

View File

@ -0,0 +1,169 @@
---
title: JavaScript Data Structures - Graph
shortTitle: Graph
type: story
language: javascript
tags: [class]
author: chalarangelo
cover: purple-flower-macro-1
excerpt: A graph is a data structure consisting of a set of vertices connected by a set of edges.
dateModified: 2021-08-17T05:00:00-04:00
---
### Definition
A graph is a data structure consisting of a set of nodes or vertices and a set of edges that represent connections between those nodes. Graphs can be directed or undirected, while their edges can be assigned numeric weights.
![JavaScript Graph visualization](./illustrations/ds-graph.png)
Each node in a graph data structure must have the following properties:
- `key`: The key of the node
- `value`: The value of the node
Each edge in a graph data structure must have the following properties:
- `a`: The starting node of the edge
- `b`: The target node of the edge
- `weight`: An optional numeric weight value for the edge
The main operations of a graph data structure are:
- `addNode`: Inserts a new node with the specific key and value
- `addEdge`: Inserts a new edge between two given nodes, optionally setting its weight
- `removeNode`: Removes the node with the specified key
- `removeEdge`: Removes the edge between two given nodes
- `findNode`: Retrieves the node with the given key
- `hasEdge`: Checks if the graph has an edge between two given nodes
- `setEdgeWeight`: Sets the weight of a given edge
- `getEdgeWeight`: Gets the weight of a given edge
- `adjacent`: Finds all nodes for which an edge exists from a given node
- `indegree`: Calculates the total number of edges to a given node
- `outdegree`: Calculates the total number of edges from a given node
### Implementation
```js
class Graph {
constructor(directed = true) {
this.directed = directed;
this.nodes = [];
this.edges = new Map();
}
addNode(key, value = key) {
this.nodes.push({ key, value });
}
addEdge(a, b, weight) {
this.edges.set(JSON.stringify([a, b]), { a, b, weight });
if (!this.directed)
this.edges.set(JSON.stringify([b, a]), { a: b, b: a, weight });
}
removeNode(key) {
this.nodes = this.nodes.filter(n => n.key !== key);
[...this.edges.values()].forEach(({ a, b }) => {
if (a === key || b === key) this.edges.delete(JSON.stringify([a, b]));
});
}
removeEdge(a, b) {
this.edges.delete(JSON.stringify([a, b]));
if (!this.directed) this.edges.delete(JSON.stringify([b, a]));
}
findNode(key) {
return this.nodes.find(x => x.key === key);
}
hasEdge(a, b) {
return this.edges.has(JSON.stringify([a, b]));
}
setEdgeWeight(a, b, weight) {
this.edges.set(JSON.stringify([a, b]), { a, b, weight });
if (!this.directed)
this.edges.set(JSON.stringify([b, a]), { a: b, b: a, weight });
}
getEdgeWeight(a, b) {
return this.edges.get(JSON.stringify([a, b])).weight;
}
adjacent(key) {
return [...this.edges.values()].reduce((acc, { a, b }) => {
if (a === key) acc.push(b);
return acc;
}, []);
}
indegree(key) {
return [...this.edges.values()].reduce((acc, { a, b }) => {
if (b === key) acc++;
return acc;
}, 0);
}
outdegree(key) {
return [...this.edges.values()].reduce((acc, { a, b }) => {
if (a === key) acc++;
return acc;
}, 0);
}
}
```
- Create a `class` with a `constructor` that initializes an empty array, `nodes`, and a `Map`, `edges`, for each instance. The optional argument, `directed`, specifies if the graph is directed or not.
- Define an `addNode()` method, which uses `Array.prototype.push()` to add a new node in the `nodes` array.
- Define an `addEdge()` method, which uses `Map.prototype.set()` to add a new edge to the `edges` Map, using `JSON.stringify()` to produce a unique key.
- Define a `removeNode()` method, which uses `Array.prototype.filter()` and `Map.prototype.delete()` to remove the given node and any edges connected to it.
- Define a `removeEdge()` method, which uses `Map.prototype.delete()` to remove the given edge.
- Define a `findNode()` method, which uses `Array.prototype.find()` to return the given node, if any.
- Define a `hasEdge()` method, which uses `Map.prototype.has()` and `JSON.stringify()` to check if the given edge exists in the `edges` Map.
- Define a `setEdgeWeight()` method, which uses `Map.prototype.set()` to set the weight of the appropriate edge, whose key is produced by `JSON.stringify()`.
- Define a `getEdgeWeight()` method, which uses `Map.prototype.get()` to get the eight of the appropriate edge, whose key is produced by `JSON.stringify()`.
- Define an `adjacent()` method, which uses `Map.prototype.values()`, `Array.prototype.reduce()` and `Array.prototype.push()` to find all nodes connected to the given node.
- Define an `indegree()` method, which uses `Map.prototype.values()` and `Array.prototype.reduce()` to count the number of edges to the given node.
- Define an `outdegree()` method, which uses `Map.prototype.values()` and `Array.prototype.reduce()` to count the number of edges from the given node.
```js
const g = new Graph();
g.addNode('a');
g.addNode('b');
g.addNode('c');
g.addNode('d');
g.addEdge('a', 'c');
g.addEdge('b', 'c');
g.addEdge('c', 'b');
g.addEdge('d', 'a');
g.nodes.map(x => x.value); // ['a', 'b', 'c', 'd']
[...g.edges.values()].map(({ a, b }) => `${a} => ${b}`);
// ['a => c', 'b => c', 'c => b', 'd => a']
g.adjacent('c'); // ['b']
g.indegree('c'); // 2
g.outdegree('c'); // 1
g.hasEdge('d', 'a'); // true
g.hasEdge('a', 'd'); // false
g.removeEdge('c', 'b');
[...g.edges.values()].map(({ a, b }) => `${a} => ${b}`);
// ['a => c', 'b => c', 'd => a']
g.removeNode('c');
g.nodes.map(x => x.value); // ['a', 'b', 'd']
[...g.edges.values()].map(({ a, b }) => `${a} => ${b}`);
// ['d => a']
g.setEdgeWeight('d', 'a', 5);
g.getEdgeWeight('d', 'a'); // 5
```

View File

@ -0,0 +1,142 @@
---
title: JavaScript Data Structures - Linked List
shortTitle: Linked List
type: story
language: javascript
tags: [class]
author: chalarangelo
cover: purple-flower-macro-3
excerpt: A linked list is a linear data structure where each element points to the next.
dateModified: 2021-08-08T05:00:00-04:00
---
### Definition
A linked list is a linear data structure that represents a collection of elements, where each element points to the next one. The first element in the linked list is the head and the last element is the tail.
![JavaScript Linked List visualization](./illustrations/ds-linked-list.png)
Each element of a linked list data structure must have the following properties:
- `value`: The value of the element
- `next`: A pointer to the next element in the linked list (`null` if there is none)
The main properties of a linked list data structure are:
- `size`: The number of elements in the linked list
- `head`: The first element in the linked list
- `tail`: The last element in the linked list
The main operations of a linked list data structure are:
- `insertAt`: Inserts an element at the specific index
- `removeAt`: Removes the element at the specific index
- `getAt`: Retrieves the element at the specific index
- `clear`: Empties the linked list
- `reverse`: Reverses the order of elements in the linked list
### Implementation
```js
class LinkedList {
constructor() {
this.nodes = [];
}
get size() {
return this.nodes.length;
}
get head() {
return this.size ? this.nodes[0] : null;
}
get tail() {
return this.size ? this.nodes[this.size - 1] : null;
}
insertAt(index, value) {
const previousNode = this.nodes[index - 1] || null;
const nextNode = this.nodes[index] || null;
const node = { value, next: nextNode };
if (previousNode) previousNode.next = node;
this.nodes.splice(index, 0, node);
}
insertFirst(value) {
this.insertAt(0, value);
}
insertLast(value) {
this.insertAt(this.size, value);
}
getAt(index) {
return this.nodes[index];
}
removeAt(index) {
const previousNode = this.nodes[index - 1];
const nextNode = this.nodes[index + 1] || null;
if (previousNode) previousNode.next = nextNode;
return this.nodes.splice(index, 1);
}
clear() {
this.nodes = [];
}
reverse() {
this.nodes = this.nodes.reduce(
(acc, { value }) => [{ value, next: acc[0] || null }, ...acc],
[]
);
}
*[Symbol.iterator]() {
yield* this.nodes;
}
}
```
- Create a `class` with a `constructor` that initializes an empty array, `nodes`, for each instance.
- Define a `size` getter, that returns that uses `Array.prototype.length` to return the number of elements in the `nodes` array.
- Define a `head` getter, that returns the first element of the `nodes` array or `null` if empty.
- Define a `tail` getter, that returns the last element of the `nodes` array or `null` if empty.
- Define an `insertAt()` method, which uses `Array.prototype.splice()` to add a new object in the `nodes` array, updating the `next` key of the previous element.
- Define two convenience methods, `insertFirst()` and `insertLast()` that use the `insertAt()` method to insert a new element at the start or end of the `nodes` array respectively.
- Define a `getAt()` method, which retrieves the element in the given `index`.
- Define a `removeAt()` method, which uses `Array.prototype.splice()` to remove an object in the `nodes` array, updating the `next` key of the previous element.
- Define a `clear()` method, which empties the `nodes` array.
- Define a `reverse()` method, which uses `Array.prototype.reduce()` and the spread operator (`...`) to reverse the order of the `nodes` array, updating the `next` key of each element appropriately.
- Define a generator method for `Symbol.iterator`, which delegates to the `nodes` array's iterator using the `yield*` syntax.
```js
const list = new LinkedList();
list.insertFirst(1);
list.insertFirst(2);
list.insertFirst(3);
list.insertLast(4);
list.insertAt(3, 5);
list.size; // 5
list.head.value; // 3
list.head.next.value; // 2
list.tail.value; // 4
[...list.map(e => e.value)]; // [3, 2, 1, 5, 4]
list.removeAt(1); // 2
list.getAt(1).value; // 1
list.head.next.value; // 1
[...list.map(e => e.value)]; // [3, 1, 5, 4]
list.reverse();
[...list.map(e => e.value)]; // [4, 5, 1, 3]
list.clear();
list.size; // 0
```

View File

@ -0,0 +1,76 @@
---
title: JavaScript Data Structures - Queue
shortTitle: Queue
type: story
language: javascript
tags: [class]
author: chalarangelo
cover: purple-flower-macro-2
excerpt: A queue is a linear data structure which follows a first in, first out (FIFO) order of operations.
dateModified: 2021-07-29T05:00:00-04:00
---
### Definition
A queue is a linear data structure that behaves like a real-world queue. It follows a first in, first out (FIFO) order of operations, similar to its real-world counterpart. This means that new items are added to the end of the queue, whereas items are removed from the start of the queue.
![JavaScript Queue visualization](./illustrations/ds-queue.png)
The main operations of a queue data structure are:
- `enqueue`: Adds an element to the end of the queue
- `dequeue`: Removes an element from the start of the queue
- `peek`: Retrieves the element at the start of the queue, without removing it
- `isEmpty`: Checks if the queue is empty
### Implementation
```js
class Queue {
constructor() {
this.items = [];
}
enqueue(item) {
this.items.push(item);
}
dequeue(item) {
return this.items.shift();
}
peek(item) {
return this.items[0];
}
isEmpty() {
return this.items.length === 0;
}
}
```
- Create a `class` with a `constructor` that initializes an empty array, `items`, for each instance.
- Define an `enqueue()` method, which uses `Array.prototype.push()` to add an element to the end of the `items` array.
- Define a `dequeue()` method, which uses `Array.prototype.shift()` to remove an element from the start of the `items` array.
- Define a `peek()` method, which retrieves the value of the first element in the `items` array, without removing it.
- Define an `isEmpty()` method, which uses `Array.prototype.length` to determine if the `items` array is empty.
```js
const queue = new Queue();
queue.isEmpty(); // true
queue.enqueue('A');
queue.enqueue('B');
queue.enqueue('C');
queue.enqueue('D');
queue.enqueue('E');
queue.isEmpty(); // false
queue.peek(); // 'A'
queue.dequeue(); // 'A'
queue.dequeue(); // 'B'
queue.dequeue(); // 'C'
```

View File

@ -0,0 +1,74 @@
---
title: JavaScript Data Structures - Stack
shortTitle: Stack
type: story
language: javascript
tags: [class]
author: chalarangelo
cover: purple-flower-macro-1
excerpt: A stack is a linear data structure which follows a last in, first out (LIFO) order of operations.
dateModified: 2021-08-03T05:00:00-04:00
---
### Definition
A stack is a linear data structure that behaves like a real-world stack of items. It follows a last in, first out (LIFO) order of operations, similar to its real-world counterpart. This means that new items are added to the top of the stack and items are removed from the top of the stack as well.
![JavaScript Stack visualization](./illustrations/ds-stack.png)
The main operations of a stack data structure are:
- `push`: Adds an element to the top of the stack
- `pop`: Removes an element from the top of the stack
- `peek`: Retrieves the element at the top of the stack, without removing it
- `isEmpty`: Checks if the stack is empty
### Implementation
```js
class Stack {
constructor() {
this.items = [];
}
push(item) {
this.items.unshift(item);
}
pop(item) {
return this.items.shift();
}
peek(item) {
return this.items[0];
}
isEmpty() {
return this.items.length === 0;
}
}
```
- Create a `class` with a `constructor` that initializes an empty array, `items`, for each instance.
- Define a `push()` method, which uses `Array.prototype.unshift()` to add an element to the start of the `items` array.
- Define a `pop()` method, which uses `Array.prototype.shift()` to remove an element from the start of the `items` array.
- Define a `peek()` method, which retrieves the value of the first element in the `items` array, without removing it.
- Define an `isEmpty()` method, which uses `Array.prototype.length` to determine if the `items` array is empty.
```js
const stack = new Stack();
stack.push('apples');
stack.push('oranges');
stack.push('pears');
stack.isEmpty(); // false
stack.peek(); // 'pears'
stack.pop(); // 'pears'
stack.pop(); // 'oranges'
stack.pop(); // 'apples'
stack.isEmpty(); // true
```

View File

@ -0,0 +1,138 @@
---
title: JavaScript Data Structures - Tree
shortTitle: Tree
type: story
language: javascript
tags: [class]
author: chalarangelo
cover: purple-flower-macro-2
excerpt: A tree is a data structure consisting of a set of linked nodes representing a hierarchical tree structure.
dateModified: 2021-08-22T05:00:00-04:00
---
### Definition
A tree is a data structure consisting of a set of linked nodes that represent a hierarchical tree structure. Each node is linked to others via parent-children relationship. The first node in the tree is the root, whereas nodes without any children are the leaves.
![JavaScript Tree visualization](./illustrations/ds-tree.png)
Each node in a tree data structure must have the following properties:
- `key`: The key of the node
- `value`: The value of the node
- `parent`: The parent of the node (`null` if there is none)
- `children`: An array of pointers to the node's children
The main operations of a tree data structure are:
- `insert`: Inserts a node as a child of the given parent node
- `remove`: Removes a node and its children from the tree
- `find`: Retrieves a given node
- `preOrderTraversal`: Traverses the tree by recursively traversing each node followed by its children
- `postOrderTraversal`: Traverses the tree by recursively traversing each node's children followed by the node
### Implementation
```js
class TreeNode {
constructor(key, value = key, parent = null) {
this.key = key;
this.value = value;
this.parent = parent;
this.children = [];
}
get isLeaf() {
return this.children.length === 0;
}
get hasChildren() {
return !this.isLeaf;
}
}
class Tree {
constructor(key, value = key) {
this.root = new TreeNode(key, value);
}
*preOrderTraversal(node = this.root) {
yield node;
if (node.children.length) {
for (let child of node.children) {
yield* this.preOrderTraversal(child);
}
}
}
*postOrderTraversal(node = this.root) {
if (node.children.length) {
for (let child of node.children) {
yield* this.postOrderTraversal(child);
}
}
yield node;
}
insert(parentNodeKey, key, value = key) {
for (let node of this.preOrderTraversal()) {
if (node.key === parentNodeKey) {
node.children.push(new TreeNode(key, value, node));
return true;
}
}
return false;
}
remove(key) {
for (let node of this.preOrderTraversal()) {
const filtered = node.children.filter(c => c.key !== key);
if (filtered.length !== node.children.length) {
node.children = filtered;
return true;
}
}
return false;
}
find(key) {
for (let node of this.preOrderTraversal()) {
if (node.key === key) return node;
}
return undefined;
}
}
```
- Create a `class` for the `TreeNode` with a `constructor` that initializes the appropriate `key`, `value`, `parent` and `children` properties.
- Define an `isLeaf` getter, that uses `Array.prototype.length` to check if `children` is empty.
- Define a `hasChildren` getter, that is the reverse of the `isLeaf` getter.
- Create a `class` for the `Tree` with a `constructor` that initializes the `root` of the tree.
- Define a `preOrderTraversal()` generator method that traverses the tree in pre-order, using the `yield*` syntax to recursively delegate traversal to itself.
- Define a `postOrderTraversal()` generator method that traverses the tree in post-order, using the `yield*` syntax to recursively delegate traversal to itself.
- Define an `insert()` method, that uses the `preOrderTraversal()` method and `Array.prototype.push()` to add a new `TreeNode` to the tree.
- Define a `remove()` method, that uses the `preOrderTraversal()` method and `Array.prototype.filter()` to remove a `TreeNode` from the tree.
- Define a `find()` method, that uses the `preOrderTraversal()` method to retrieve the given node in the tree.
```js
const tree = new Tree(1, 'AB');
tree.insert(1, 11, 'AC');
tree.insert(1, 12, 'BC');
tree.insert(12, 121, 'BG');
[...tree.preOrderTraversal()].map(x => x.value);
// ['AB', 'AC', 'BC', 'BCG']
tree.root.value; // 'AB'
tree.root.hasChildren; // true
tree.find(12).isLeaf; // false
tree.find(121).isLeaf; // true
tree.find(121).parent.value; // 'BC'
tree.remove(12);
[...tree.postOrderTraversal()].map(x => x.value);
// ['AC', 'AB']
```

View File

@ -0,0 +1,29 @@
---
title: "Tip: Convert decimal number to hexadecimal"
shortTitle: Decimal to hexadecimal
type: tip
language: javascript
tags: [math]
author: chalarangelo
cover: waves-from-above
excerpt: Ever needed to convert a decimal number to hexadecimal? Here's a quick and easy way to do it.
dateModified: 2022-09-21T05:00:00-04:00
---
Numeric values are represented in decimal format by default, when converted to strings. If you want to display them in hexadecimal format, you can use `Number.prototype.toString()` and pass the base you want to use (`16`) as an argument.
```js
const decimalToHex = dec => dec.toString(16);
decimalToHex(0); // '0'
decimalToHex(255); // 'ff'
```
Conversely, the opposite might also be needed. You can use `parseInt()` to convert a string to a number in a given base. If you don't specify a base, it will default to `10`.
```js
const hexToDecimal = hex => parseInt(hex, 16);
hexToDecimal('0'); // 0
hexToDecimal('ff'); // 255
```

View File

@ -0,0 +1,34 @@
---
title: How can I detect an undefined object property in JavaScript?
shortTitle: Detect undefined object property
type: story
language: javascript
tags: [object]
author: chalarangelo
cover: pink-flower
excerpt: Learn how to detect `undefined` object properties in JavaScript the correct way.
dateModified: 2022-08-07T05:00:00-04:00
---
Its not uncommon to want to detect object properties with a value of `undefined`. While this seems straightforward on the surface, `undefined` is a rather elusive value to check for.
More often than not solutions point towards direct strict comparison with `undefined` or using `typeof`. Both of these methods have a hard time differentiating between present properties with a value of `undefined` and non-existent properties. This, in turn, makes them prone to silent errors, shall you misspell the property altogether.
```js
const obj = { prop : undefined };
obj.prop === undefined; // true
typeof obj.prop === 'undefined'; // true
obj.porp === undefined; // true
typeof obj.porp === 'undefined'; // true
```
To alleviate the issue, `Object.prototype.hasOwnProperty()` can be used in addition to the previous method to check for the property actually being present on the object. Moreover, it can also be used for the opposite, so you can also detect non-existent properties and handle them accordingly.
```js
const hasUndefinedProperty = (obj, prop) =>
obj.hasOwnProperty(prop) && obj[prop] === undefined;
const obj = { prop: undefined };
hasUndefinedProperty(obj, 'prop'); // true
hasUndefinedProperty(obj, 'porp'); // false
```

View File

@ -0,0 +1,37 @@
---
title: What does the double negation operator do in JavaScript?
shortTitle: Double negation operator
type: question
language: javascript
tags: [type]
author: chalarangelo
cover: memories-of-pineapple-2
excerpt: You've probably come across the double negation operator (`!!`) before, but do you know what it does?
dateModified: 2022-07-26T05:00:00-04:00
---
JavaScript's negation operator (`!`) is a unary operator, used to invert the truth value of its operand. When used twice, known as the double negation operator (`!!`), it can be used to convert a value to a boolean.
```js
const x = 1;
const y = null;
!!x; // true
!!y; // false
```
Using the double negation operator is functionally equivalent to using the `Boolean()` function, which we explored in depth in a [previous article](/js/s/boolean-function). In terms of readability and usability, I would still suggest using the `Boolean()` function. It conveys the intent of the operation more clearly, and it's easier to understand at a glance.
```js
const x = 1;
const y = null;
Boolean(x); // true
Boolean(y); // false
const values = [0, 0, 2, 0, 3];
// Kinda readable, but not great
values.filter(x => !!x); // [2, 3]
// Arguably more readable
values.filter(Boolean); // [2, 3]
```

View File

@ -0,0 +1,49 @@
---
title: Chaining dynamic getters using the Proxy object
shortTitle: Dynamic getter chaining
type: story
language: javascript
tags: [object,proxy]
author: chalarangelo
cover: colorful-rocks
excerpt: Using the Proxy object, we can create chainable dynamic getters for objects in JavaScript.
dateModified: 2023-05-28T05:00:00-04:00
---
The dawn of ES6 brought about jQuery's fall from grace, as a lot of the conveniences it afforded developers were now available in the language itself. However, jQuery's API design was convenient in many ways that native JavaScript often isn't. One of the most practical things jQuery offered was its ability to chain methods together, minimizing duplication and making code more readable.
Looking at the use case at hand, I thought that I could stitch together a general-purpose solution using JavaScript's `Proxy` object. In fact, I think that the concept of dynamic getters and setters that I described in [my previous post](/js/s/dynamic-getter-setter-proxy) is a great place to start.
To recap, intercepting the behavior of the `get` and `set` traps of a `Proxy` object allows us to create dynamic accessors for objects. This is particularly useful when the shape of the data is not known in advance, or when the value of a property needs to be manipulated before it is returned.
In our scenario, we want each property to be accessed as a function. The function should do one of two things, depending on the argument it receives:
- If the argument is `undefined`, then the property should be returned as-is.
- If the argument is any other value, the property should be set to the value of the argument and a proxy of the object should be returned.
Given these requirements, we can define the handler's behavior for the `get` trap. All we need to do is check if the argument is `undefined` and choose which behavior to perform. Here's what the code looks like:
```js
const getHandler = {
get: (target, prop) => {
return value => {
if (typeof value !== 'undefined') {
target[prop] = value;
return new Proxy(target, getHandler);
}
return target[prop];
};
}
};
const styles = {};
const proxiedStyles = new Proxy(styles, getHandler);
proxiedStyles.color('#101010').background('#fefefe').margin('4px 8px');
proxiedStyles.color(); // #101010
proxiedStyles.background(); // #fefefe
proxiedStyles.margin(); // 4px 8px
```
As you can see the handler is pretty short and straightforward. The only confusing step is the creation of a new `Proxy` inside the trap. As we don't have a reference to the proxy object itself inside the trap, we need to create a new one. This is necessary because we want to return a new proxy to allow for chaining. If we didn't do this, the proxy would only be able to be used once.

View File

@ -0,0 +1,76 @@
---
title: Can I create dynamic setters and getters in JavaScript?
shortTitle: Dynamic getters and setters
type: question
language: javascript
tags: [object,proxy]
author: chalarangelo
cover: green-cabin-cow
excerpt: Using the Proxy object, we can create dynamic getters and setters for objects in JavaScript.
dateModified: 2023-04-09T05:00:00-04:00
---
Sometimes, when working with objects, the shape of the data is not always known. It might also be inefficient to add special getters for each property of an object, especially if the object is very large. Moreover, if keys are expected to follow a pattern, there are infinite potential key names, the value of which is impossible to validate via the use of setters.
These are only some use-cases where **dynamic setters and getters** could be useful. Luckily, JavaScript's `Proxy` object can be used for this purpose. Using the `get` and `set` traps, we can manipulate the object's behavior when a property is accessed or set. In this post, we will look at two simple examples to give you an idea of how this works.
Note that, in contrast to these examples, a `Proxy` object can define multiple traps to intercept many different operations on the target object.
### Dynamic getters
A **dynamic getter** is a getter that is not explicitly defined for a property, but is instead created on the fly when the property is accessed. This is particularly useful when the shape of the data is not known in advance, or when the value of a property needs to be manipulated before it is returned.
In this example, we will be creating a proxy that will manipulate string values in the target object. The proxy will trim any string values that are accessed, and return the value as-is for any other type of value. Finally, non-existent properties will return `undefined`, as expected.
```js
const obj = { foo: 'bar ', baz: ' qux ', quux: 1 };
const proxiedObj = new Proxy(obj, {
get(target, prop) {
if (prop in target && typeof target[prop] === 'string')
return target[prop].trim();
return target[prop];
}
});
proxiedObj.foo; // 'bar'
proxiedObj.baz; // 'qux'
proxiedObj.quux; // 1
proxiedObj.quuz; // undefined
```
While this is a simple example, it highlights the power of the `Proxy` object. In this case, we are able to manipulate the behavior of the object without having to define a getter for each property. This will also apply to any new properties that are added to the object, as the proxy will be able to intercept the access and return the appropriate value.
### Dynamic setters
A **dynamic setter** is a setter that is not explicitly defined for a property, but is instead created on the fly when the property is set. This can be very useful if the object's keys follow a certain pattern or certain conditions apply to all values that are set.
In this example, we will be creating a proxy that will only allow setting of properties that correspond to a date in the format `yyyy-mm-dd`. Additionally, if a property is already set, its value should be impossible to change. This could be useful if, for example, you were creating something akin to a read-only log.
```js
const obj = {};
const proxiedObj = new Proxy(obj, {
set(target, prop, value) {
if (prop in target) return false;
if (typeof prop === 'string' && prop.match(/^\d{4}-\d{2}-\d{2}$/)) {
target[prop] = value;
return true;
}
return false;
}
});
proxiedObj['2023-01-01'] = 1;
proxiedObj['2023-01-01'] = 2; // This will fail, the property is already set
proxiedObj['2023-ab-cd'] = 1; // This will fail, the property name is not a date
proxiedObj; // { '2023-01-01': 1 }
```
As shown in this example, the `Proxy` object can be used to validate keys as well as values when setting properties. In this case, we were able to prevent the value of a property from being changed, as well as prevent the setting of properties that do not follow the expected pattern.
As a side note, remember that the regular expression used here is not a full date validation, but only checks for a simple pattern to demonstrate the concept. If you need to validate dates in a production environment, this is not the way to go.
### Conclusion
As shown in this post, the `Proxy` object provides a particularly powerful way to manipulate the behavior of objects. That being said, you might want to consider your specific use-case before reaching for this tool. Dynamic getters and setters can be very useful, but they can also cause a lot of headaches if used incorrectly.

View File

@ -0,0 +1,37 @@
---
title: "Tip: Optimize dynamically added object properties"
shortTitle: Dynamically added property optimization
type: tip
language: javascript
tags: [object,performance]
author: chalarangelo
cover: hiking-balance
excerpt: Dynamically adding object properties can be pretty slow in some cases. Here's how to optimize it.
dateModified: 2022-11-02T05:00:00-04:00
---
JavaScript is one of the most flexible languages out there, but sometimes this comes with performance costs attached. One such example is the use of dynamically added properties to objects. Oddly enough, this performance impact comes from JavaScript engines optimizing for static typing.
The V8 engine, which powers Chrome and Node.js, uses **shapes** and **transition chains** to optimize object property access. When two objects have the same properties, they are considered to have the same shape. Adding new properties to an object, creates a transition chain to add the new property. Without getting into further detail, it's obvious that a simple shape is faster to access than a transition chain.
```js
// Create an object with a single property
const obj = { a: 1 };
// Add a new property to the object
obj.b = 2;
// Access a property of the object
console.log(obj.a);
```
Circling back to dynamically added properties, the engine cannot know ahead of time what properties will be added to the object. Thus, it ends up creating a transition chain for each new property that is added. This is not a big deal for a few properties, but it can become a problem when adding a lot of properties to an object.
Luckily, this is easy to fix. The easiest solution is to **define all possible properties of an object ahead of time** and give them an empty value (i.e. `null` or `undefined`). This way, the engine can create a shape for the object and optimize property access. This is not always possible, but it's a good practice to follow.
```js
// Create an object with all possible properties
const obj = { a: 1, b: undefined };
// Add a new property to the object
obj.b = 2;
// Access a property of the object
console.log(obj.a);
```

View File

@ -0,0 +1,41 @@
---
title: "Tip: Edit URL Parameters in JavaScript"
shortTitle: Edit URL Parameters
type: tip
language: javascript
tags: [string]
author: chalarangelo
cover: sofia-tram
excerpt: Avoid the naive approach and use a more robust method to edit URL parameters in JavaScript.
dateModified: 2022-12-07T05:00:00-04:00
---
Editing the query string of a URL in JavaScript is pretty common. While the naive approach of directly editing the URL as a string often works, it's a fragile solution that can break easily. This is especially true when working with encoding, hash fragments and other such intricacies.
The most robust way to go about editing a URL is using the `URL` interface to parse the original URL string and edit it as necessary. This way, the browser will take care of all the complicated details and make the code easier to read and maintain.
```js
const urlString = 'https://mysite.com?p=42&from=home#details';
const url = new URL(urlString);
// Delete a parameter
const removedParam = 'from';
url.searchParams.delete(removedParam);
// Edit/add parameters
const newParams = {
p: 57,
track: 'none'
};
Object.keys(newParams).forEach(key => {
url.searchParams.set(key, newParams[key]);
});
// Edit the hash fragment
const newHash = 'new';
url.hash = newHash;
console.log(`${url}`); // https://mysite.com?p=57&track=none#new
```
As you can see in the example, the `URL` interface provides a number of methods to edit the URL. The most commonly used ones are `URL.searchParams` and `URL.hash`. The former is a `URLSearchParams` object that provides methods to edit the query string of the URL, while the latter is a string that contains the hash fragment of the URL. Apart from these two, the `URL` interface also provides methods to edit the protocol, host, port, path, etc. of the URL.

View File

@ -0,0 +1,23 @@
---
title: "Tip: Element at a specific point on the page"
shortTitle: Element at specific coordinates
type: tip
language: javascript
tags: [browser]
author: chalarangelo
cover: armchair-in-yellow
excerpt: Using `Document.elementFromPoint()` to easily get the element at a specific point on the page.
dateModified: 2022-12-18T05:00:00-04:00
---
Figuring out where an element is located on the page with JavaScript can be tricky. Such needs often arise when working with pointer events or other forms of user input. As expected, such a common problem has many different viable solutions using well-established web APIs.
As I recently discovered, `Document.elementFromPoint()` provides a pretty interesting and straightforward solution. It allows you to get the element at a specific point on the page and it also works quite well with `iframe`s, too. Additionally, `Document.elementsFromPoint()` provides similar functionality, but returns an array of all the elements at a specific point on the page, in order of their z-index.
```js
// Returns the topmost element at the specified coordinates
const element = document.elementFromPoint(x, y);
// Returns an array of all the elements at the specified coordinates
const elements = document.elementsFromPoint(x, y);
```

View File

@ -0,0 +1,42 @@
---
title: Can I validate an email address in JavaScript?
shortTitle: Email address validation
type: question
language: javascript
tags: [string,regexp]
author: chalarangelo
cover: blank-card
excerpt: Email address validation can be much trickier than it sounds. Here's why and my advice on how to approach this problem.
dateModified: 2022-10-05T05:00:00-04:00
---
One of the most frequent code snippet requests I get is about a function that can validate email addresses. While it sounds easy, Ive been putting off writing about this topic for a while. The reason is that there are **no great solutions to this sort of problem**.
Theoretically, email addresses can be validated using a regular expression. After all, there are a couple of simple rules, such as the presence of the at (`@`) symbol and an appropriate domain, right? Yes, but there are tons of valid email address that dont look exactly like that. In fact, theres a whole standard, [RFC 2822](https://www.rfc-editor.org/rfc/rfc2822#section-3.4.1), that defines what email addresses can look like. Then, theres the issue of what different mail servers will allow. Gmail, for example, will not allow underscores (`_`) or more than one period (`.`) in a row.
Additionally, even if you could validate an email address, theres **no way of knowing if this address is in fact currently in use**. The only way to do so, is to send an email and check the response. This is why most websites and apps nowadays send you a confirmation email in the first place.
Finally, even if you used a regular expression that is compliant with RFC 2822, it wouldnt be without issues. Understanding how it works or figuring out if it works correctly for each and every case would be pretty difficult. More importantly, though, it could be prone to **regular expression denial of service (ReDoS) attacks**, if implemented incorrectly.
By now, you should be starting to figure out why Ive been hesitant to showcase a solution to the problem of email validation. While solutions do exist, the implications of each one must be considered carefully.
My suggestion would be to **check for basic structural elements** on the frontend, then **send a confirmation email** from your server to check that the email address is in use. Its easy to implement and gets the job done. Going back to the original idea, heres a simple function that checks for the most common syntax errors.
```js
const isEmailValid = address => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(address);
isEmailValid('abcd@site.com'); // true
isEmailValid('ab_c@site.com'); // true
isEmailValid('ab.c@site.com'); // true
isEmailValid('a@my.site.com'); // true
isEmailValid('ab c@site.com'); // false
isEmailValid('ab@c@site.com'); // false
isEmailValid('abcde@sitecom'); // false
isEmailValid('abcdesite.com'); // false
```
This regular expression is pretty short, yet it validates the following conditions:
- No whitespace characters anywhere in the email address
- Presence of a single at (`@`) character before the domain part
- At least one dot (`.`) character in the domain part

View File

@ -0,0 +1,26 @@
---
title: Faster element removal in unordered JavaScript arrays
shortTitle: Faster element removal in unordered arrays
type: story
language: javascript
tags: [array]
author: chalarangelo
cover: purple-flower-bunch
excerpt: Are you performing a lot of array operations? Maybe element removal is a performance bottleneck you can avoid.
dateModified: 2022-03-20T05:00:00-04:00
---
`Array.prototype.splice()` is the most commonly-used way to remove elements from an array. Turns out that its not the fastest, though. This can be an especially important factor contributing to your codes performance, if you are performing many operations with large arrays.
Theres a pretty easy trick that you can use to speed up this operation, but the order of elements in the array must not play a role for it to work. Provided the latter is true, you can swap two elements in the array without any issues. This means you can swap any element with the last one, for example. But removing the last element is easy and fast, using `Array.prototype.pop()`, so you can use that to your advantage. For example:
```js
const arr = [3, 1, 5, 7, 9]; // Want to remove 5 (index: 2)
arr[2] = arr[arr.length -1]; // Copy last element to 3rd place
arr.pop(); // Remove the last element
```
In this example, we want to remove an element which is in the middle of the array. We would start by performing a swap of the element we want to remove with the last one. However, we dont need to actually swap them. All we need to do is ensure that the last element goes in the place of the one we want to remove. We can simply copy it to that position and then use `Array.prototype.pop()` to remove the last element.
On a side note, one could think shortening this to `arr[i] = arr.pop()` would make it even terser. Turns out this isnt the case, as this shorter version will fail if we try to remove the last element in the array.

View File

@ -0,0 +1,61 @@
---
title: Frequency Map Data Structure
shortTitle: Frequency Map
type: story
language: javascript
tags: [class]
author: chalarangelo
cover: radio-monstera
excerpt: A custom data structure to keep track of value frequencies in an array.
dateModified: 2022-11-13T05:00:00-04:00
---
Counting the frequency of unique values in an array is reasonably easy, as demonstrated in the [frequencies snippet](/js/s/frequencies). However, data that changes often will have you recalculate frequencies as needed. This can become tedious and inefficient, especially if you only need to keep track of the frequencies and have no need for the original array.
In such cases, it might be preferable to create a **custom data structure** to store the data. This data structure will be able to **keep track of the frequencies of the values** it contains and update them as needed. Here's how you can implement such a data structure:
```js
class FrequencyMap extends Map {
constructor(iterable) {
super();
iterable.forEach(value => this.add(value));
}
set() {
throw new Error('Please use Map.prototype.add() instead.');
}
add(value) {
if (this.has(value)) super.set(value, this.get(value) + 1);
else super.set(value, 1);
return this;
}
delete(value) {
if (this.get(value) === 1) super.delete(value);
else super.set(value, this.get(value) - 1);
return this;
}
sorted(ascending = true) {
if (ascending) return [...this].sort((a, b) => a[1] - b[1]).map(v => v[0]);
else return [...this].sort((a, b) => b[1] - (1)[1]).map(v => v[0]);
}
}
```
- Leverage the built-in `Map` class via the use of inheritance.
- Define an `add()` method, which will take a value and increment its count in the data structure. Use `Map.prototype.has()` to check if the value already exists and act accordingly.
- Extend `Map.prototype.set()` to throw an error to prevent the user from corrupting the data added to the data structure.
- Extend `Map.prototype.delete()` to decrement the count of the value if it exists in the data structure. Use `Map.prototype.has()` to check if the value's frequency is `1` and delete it if necessary.
- As the data structure operates more like a `Set`, after the `constructor` to accept an array of values. Use `Array.prototype.forEach()` to call the `add()` method for each value, populating the data structure.
- Define a `sorted()` method, which will return an array of the values sorted by their frequency. Use `Array.prototype.sort()` to sort the values by their frequency and `Array.prototype.map()` to return only the values. The `ascending` argument determines the order of the returned array.
```js
const fMap = new FrequencyMap(['a', 'b', 'c', 'a', 'a', 'b']);
fMap.delete('c');
fMap.add('d');
console.log(fMap.sorted(false)); // [ 'a', 'b' , 'd' ]
```

View File

@ -0,0 +1,46 @@
---
title: Immutable JavaScript objects using the Proxy object
shortTitle: Immutable objects using Proxy
type: story
language: javascript
tags: [object,proxy]
author: chalarangelo
cover: frozen-globe
excerpt: Freezing objects is not the only way to prevent mutations. Learn how you can leverage the Proxy object to your advantage.
dateModified: 2022-04-10T05:00:00-04:00
---
Object mutability and its relation to the `const` keyword is a very common headache for developers. More often than not, when looking for ways to make an object immutable, `Object.freeze()` will pop up as a solution. Weve explored this approach previously, elaborating on deep freezing solutions. You can read more about it in [this article](/js/s/deep-freeze-object).
While `Object.freeze()` is the more straightforward approach, it might not always be the best solution. This may be especially true when dealing with extensive object nesting or when objects have a very short life. For such cases, a different approach using the [Proxy object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) might make more sense. Heres what that looks like:
```js
const term = {
id: 1,
value: 'hello',
properties: [{ type: 'usage', value: 'greeting' }],
};
const immutable = obj =>
new Proxy(obj, {
get(target, prop) {
return typeof target[prop] === 'object'
? immutable(target[prop])
: target[prop];
},
set() {
throw new Error('This object is immutable.');
},
});
const immutableTerm = immutable(term);
const immutableProperty = immutableTerm.properties[0];
immutableTerm.name = 'hi'; // Error: This object is immutable.
immutableTerm.id = 2; // Error: This object is immutable.
immutableProperty.value = 'pronoun'; // Error: This object is immutable.
```
Even though proxies are not all that common, this code shouldnt be hard to understand. The gist of the idea is that you use a handler to prevent mutations to the object via the `set()` trap. Additionally, you use the `get()` trap to enforce immutability on all nested values. This is done by checking the type of the value and applying the proxy to nested objects.
Thats pretty much all there is to it. With just a few lines of code, you now have a way to prevent mutation on an object, regardless of shape, nesting or complexity.

View File

@ -54,7 +54,7 @@ for (let item of myList) {
}
```
In the above example, we implement a [`LinkedList` data structure](/articles/s/js-data-structures-linked-list), that internally uses a `data` array. Each item in it has a `value` and some implementation-specific properties used to determine its position in the sequence. Objects constructed from this class are not iterable by default. To define an iterator we use `Symbol.iterator` and set it up so that the returned sequence is in order based on the internal implementation of the class, while the returned items only return their `value`.
In the above example, we implement a [`LinkedList` data structure](/js/s/data-structures-linked-list), that internally uses a `data` array. Each item in it has a `value` and some implementation-specific properties used to determine its position in the sequence. Objects constructed from this class are not iterable by default. To define an iterator we use `Symbol.iterator` and set it up so that the returned sequence is in order based on the internal implementation of the class, while the returned items only return their `value`.
On a related note, iterators are just functions, meaning they can be called like any other function (e.g. to delegate the iteration to an existing iterator), while also not being restricted to the `Symbol.iterator` name. This allows us to define multiple iterators for the same object. Here's an example of these concepts at play:

View File

@ -0,0 +1,25 @@
---
title: "Tip: Get the last element of a JavaScript array"
shortTitle: Last element of array
type: tip
language: javascript
tags: [array]
author: chalarangelo
cover: purple-laptop
excerpt: Array destructuring can be leveraged in many different ways. Here's one of them.
dateModified: 2022-08-28T05:00:00-04:00
---
If you have worked with JavaScript arrays before, you might know that they can be destructured much like objects. This is most commonly used to extract the first value of an array or the values of an array with a known length.
But destructuring can go much further, as it allows you to extract the `length` property of an array. Add this to the fact that extracted variables can be used in the destructuring assignment itself and you can put together a one-liner to extract the last element of an array.
```js
const arr = [1, 2, 3];
const { 0: first, length, [length - 1]: last } = arr;
first; // 1
last; // 3
length; // 3
```
While this technique is interesting, it has a couple of caveats. First off, you have to extract the `length` property, which creates an additional variable for it. And secondly, it doesn't have any significant performance advantages over other options, such as using `Array.prototype.slice()`.

View File

@ -0,0 +1,49 @@
---
title: How can I truncate a string accounting for locale?
shortTitle: Locale-sensitive string truncation
type: question
language: javascript
tags: [string]
author: chalarangelo
cover: reflection-on-lake
excerpt: Locale-sensitive string splitting and truncation are finally possible in JavaScript.
dateModified: 2022-12-04T05:00:00-04:00
---
Breaking a string into words is not the easiest, neither is finding a good place to add an ellipsis. Part of the problem is recognizing word boundaries and words themselves. Luckily `Intl.Segmenter` is a relatively new object that enables **locale-sensitive text segmentation**.
`Intl.Segmenter` allows you to specify a locale and a `granularity` option to specify how a string should be segmented. The `granularity` option can be set to `'grapheme'`, `'word'` or `'sentence'` according to your needs. Using `Intl.Segmenter.prototype.segment()` on a string returns an iterable `Segments` object. This can then be used to find the correct index to split a string without being in the middle of a word or a sentence.
```js
const str =
'The quick brown fox jumps over the lazy dog. The jay, pig, fox, zebra and my wolves quack!';
const cutOff = 50;
const wordSegmenter = new Intl.Segmenter('en-US', { granularity: 'word' });
const sentenceSegmenter = new Intl.Segmenter('en-US', {
granularity: 'sentence',
});
let lastWordBreak = -1;
for (let word of wordSegmenter.segment(str)) {
if (word.isWordLike) continue;
if (word.index >= cutOff) break;
lastWordBreak = word.index;
}
str.slice(0, lastWordBreak) + '...';
// 'The quick brown fox jumps over the lazy dog. The...'
let lastSentenceBreak = -1;
for (let sentence of sentenceSegmenter.segment(str)) {
if (
lastSentenceBreak !== -1 &&
sentence.index + sentence.segment.length >= cutOff
)
break;
lastSentenceBreak = sentence.index + sentence.segment.length;
}
str.slice(0, lastSentenceBreak).trim().slice(0, -1) + '...';
// 'The quick brown fox jumps over the lazy dog...'
```
Note that the `Intl.Segmenter` object is not yet supported in all environments at the time of writing (December, 2022). Namely, Firefox has yet to implement it, while Node.js has only started supporting it since version 16.0.0.

View File

@ -0,0 +1,69 @@
---
title: What are the differences between Maps and objects in JavaScript?
shortTitle: Maps vs objects
type: question
language: javascript
tags: [object]
author: chalarangelo
cover: tent-stars
excerpt: Maps and objects are very similar, but they have some differences that can help you decide which one better fits your use-case.
dateModified: 2022-02-13T05:00:00-04:00
---
Most JavaScript developers are familiar with objects and probably use them every day. Maps, on the other hand, are not as common but are still very useful. While very similar to objects on the surface, they have some very important differences. Let's take a look at them.
### Key types
Object keys are limited to using only strings and symbols. Maps, on the other hand, can use values of any type as their keys, including functions and objects. This can come in handy in many different scenarios, such as memoization and data association.
```js
const people = [
{ id: 1, name: 'John', surname: 'Doe', age: 30 },
{ id: 2, name: 'Jane', surname: 'Doe', age: 28 },
];
const peopleIndex = people.reduce((index, person) => {
index[person.id] = `${person.name} ${person.surname}`;
return index;
}, {});
// peopleIndex = {
// '1': 'John Doe',
// '2': 'Jane Doe',
// }
const peopleIndexMap = new Map(
people.map(person => [person, `${person.name} ${person.surname}`])
);
// peopleIndexMap = Map {
// { id: 1, name: 'John', surname: 'Doe', age: 30 } => 'John Doe',
// { id: 2, name: 'Jane', surname: 'Doe', age: 28 } => 'Jane Doe',
// }
```
### Iteration
Object iteration is usually accomplished using `Object.keys()`, `Object.values()` or `Object.entries()`. All of these methods are available on Maps as part of their prototype, but they are significantly more efficient. The reason for this is that these Map methods return iterators, which are lazy and only iterate over the keys or values when they are needed. Additionally, Maps expose an iterator, which can be used with `for...of` loops.
```js
const obj = { a: 1, b: 2, c: 3 };
const objEntries = Object.entries(obj);
// ['a', 1], ['b', 2], ['c', 3]
for (const [key, value] of objEntries)
console.log(`${key}: ${value}`);
const map = new Map([['a', 1], ['b', 2], ['c', 3]]);
const mapEntries = [...map.entries()]; // Same as [...map]
// [['a', 1], ['b', 2], ['c', 3]]
for (const [key, value] of map)
console.log(`${key} => ${value}`);
```
### Other differences
Apart from the two main differences mentioned already, there are some other, less noticeable, ones. These include the following:
- Object size requires manual computation. Maps, on the other hand, have a built-in `size` property that can be used to track the number of key-value pairs.
- You can check for a given key's presence in an object using the `in` operator or `Object.prototype.hasOwnProperty()`. `Map.prototype.has()` accomplishes the same thing for Maps.
- Clearing an object requires manual operation and might be non-trivial in some cases. Maps solve this problem via the use of `Map.prototype.clear()`.
- Objects inherit some keys from the prototype, whereas maps do not.

View File

@ -0,0 +1,88 @@
---
title: Native JavaScript Data Structures
shortTitle: Native Data Structures
type: story
language: javascript
tags: [array]
author: chalarangelo
cover: purple-flower-macro-2
excerpt: JavaScript provides a handful of native data structures that you can start using in your code right now.
dateModified: 2021-09-05T05:00:00-04:00
---
### Arrays
An array is a linear data structure that represents a collection of elements. In JavaScript, arrays don't have a fixed size, while their contents can be of any valid type, even arrays themselves. Arrays are probably the most commonly used data structure and come with a plethora of methods that allow easy manipulation and transformation of their contents.
```js
const nums = [1, 2, 3];
const strs = Array.from('est');
nums.push(6);
nums.push(4, 9);
strs.unshift('t');
nums.length; // 6
nums[nums.length - 1]; // 9
strs[0]; // 't'
strs[2]; // 's'
nums.slice(1, 3); // [2, 3]
nums.map(n => n * 2); // [2, 4, 6, 12, 8, 18]
nums.filter(n => n % 2 === 0); // [2, 6, 4]
nums.reduce((a, n) => a + n, 0); // 25
strs.reverse(); // ['t', 's', 'e', 't']
strs.join(''); // 'test'
```
### Sets
A set is a linear data structure that represents an ordered collection of unique values. Sets in JavaScript can store any valid type of value, however each value can only occur once based on value equality checking.
```js
const nums = new Set([1, 2, 3]);
nums.add(4);
nums.add(1);
nums.add(5);
nums.add(4);
nums.size; // 5
nums.has(4); // true
nums.delete(4);
nums.has(4); // false
[...nums]; // [1, 2, 3, 5]
nums.clear();
nums.size; // 0
```
### Maps
A map is an associative data structure that represents a keyed collection of elements. Each key in a JavaScript Map has to be unique and either a primitive value or an object, whereas the values of the map can be of any valid type.
```js
const items = new Map([
[1, { name: 'John' }],
[2, { name: 'Mary' }]
]);
items.set(4, { name: 'Alan' });
items.set(2, { name: 'Jeff' });
items.size; // 3
items.has(4); // true
items.get(2); // { name: 'Jeff' }
items.delete(2);
items.size; // 2
[...items.keys()]; // [1, 4]
[...items.values()]; // [{ name: 'John' }, { name: 'Alan' }]
items.clear();
items.size; // 0
```

View File

@ -0,0 +1,36 @@
---
title: What is the difference between Object.freeze() and Object.seal() in JavaScript?
shortTitle: Object.freeze() vs Object.seal()
type: question
language: javascript
tags: [object]
author: chalarangelo
cover: frozen-globe
excerpt: Both `Object.freeze()` and `Object.seal()` serve a similar purpose, but there's one key difference you need to remember.
dateModified: 2022-02-06T05:00:00-04:00
---
Both `Object.freeze()` and `Object.seal()` serve as ways to prevent a JavaScript object from being altered. Although similar, they have a key difference that you need to remember.
```js
const frozen = Object.freeze({ username: 'johnsmith' });
const sealed = Object.seal({ username: 'johnsmith' });
frozen.name = 'John Smith'; // frozen = { username: 'johnsmith' }
sealed.name = 'John Smith'; // sealed = { username: 'johnsmith' }
delete frozen.username; // frozen = { username: 'johnsmith' }
delete sealed.username; // sealed = { username: 'johnsmith' }
frozen.username = 'jsmith'; // frozen = { username: 'johnsmith' }
sealed.username = 'jsmith'; // sealed = { username: 'jsmith' }
```
If you want to prevent new properties from being added and existing properties from being removed, then both methods will suit your needs. If, however, you want to prevent existing properties from being altered, then you have to use `Object.freeze()`. The reason for that is that `Object.seal()` only marks existing properties as non-configurable, meaning their values can be changed as long as they are writable.
| | Create | Read | Update | Delete |
| --- | --- | --- | --- | --- |
| `Object.freeze()` | No | Yes | No | No |
| `Object.seal()` | No | Yes | Yes | No |
As a closing note, remember that both methods perform a shallow freeze/seal on the object. This means that nested objects and arrays are not frozen or sealed and can be mutated. To prevent this, you can deep freeze objects, as described in [this related article](/js/s/deep-freeze-object).

View File

@ -0,0 +1,39 @@
---
title: Negative index in JavaScript array
shortTitle: Negative index array proxy
type: story
language: javascript
tags: [array,proxy]
author: chalarangelo
cover: budapest-palace
excerpt: Ever wanted to use negative indices in JavaScript arrays? Here's a simple way to do it using a Proxy.
dateModified: 2022-10-02T05:00:00-04:00
---
`Array.prototype.slice()` provides an easy way to access elements from the end of an array, using a negative `start` value. While this sounds convenient, the resulting value is an array, so it's necessary to use an index to get an individual element.
This is usually not too bad, but it's interesting to explore other options to understand the language better. In this case, we can use a `Proxy` object to allow accessing data in an array using negative indexes. To do so, an appropriate handler needs to be defined for the `get` trap.
The trap's second argument corresponds to the passed index, however it's a string, so it must first be converted to a number using `Number()`. Then, `Array.prototype.length` can be used to calculate the position of the actual element. Finally, `Reflect.get()` can be used to get the value at the specific index, but expects its second argument to be a string.
Putting everything together, this is what this looks like:
```js
const handler = {
get(target, key, receiver) {
const index = Number(key);
const prop = index < 0 ? `${target.length + index}` : key;
return Reflect.get(target, prop, receiver);
},
};
const createArray = (...elements) => {
const arr = [...elements];
return new Proxy(arr, handler);
};
let arr = createArray('a', 'b', 'c');
arr[-1]; // 'c'
arr[-1]; // 'b'
```

View File

@ -0,0 +1,55 @@
---
title: An Introduction to JavaScript Proxy
shortTitle: JavaScript Proxy Introduction
type: story
language: javascript
tags: [object,proxy,pattern]
author: chalarangelo
cover: red-mountain-range
excerpt: A quick introduction to the JavaScript Proxy object and where it can be used.
dateModified: 2023-04-02T05:00:00-04:00
---
The JavaScript `Proxy` object is a relatively new addition to the language, having been introduced in ES6. It's based on the **software design pattern** of the same name, which creates a wrapper for another object, **intercepting** and **redefining** fundamental operations for that object.
A `Proxy` object is defined as follows:
```js
const proxy = new Proxy(target, handler);
```
The `target` is the object that the proxy wraps around, while the `handler` is an object that contains one or more **"traps"** - functions that intercept operations performed on the target object.
There are a variety of available traps that can be used to customize the behavior of the target object. Here's a brief overview of all available traps and what each one does:
- `get(target, prop, receiver)` - Intercepts calls to `target[prop]`.
- `set(target, prop, value, receiver)` - Intercepts calls to `target[prop] = value`.
- `has(target, prop)` - Intercepts calls to `prop in target`.
- `apply(target, thisArg, argumentsList)` - Intercepts calls to functions.
- `construct(target, argumentsList, newTarget)` - Intercepts calls to the `new` operator.
- `defineProperty(target, prop, descriptor)` - Intercepts calls to `Object.defineProperty()`.
- `deleteProperty(target, prop)` - Intercepts calls to `delete target[prop]`.
- `getOwnPropertyDescriptor(target, prop)` - Intercepts calls to `Object.getOwnPropertyDescriptor()`.
- `ownKeys(target)` - Intercepts calls to `Object.getOwnPropertyNames()` and `Object.getOwnPropertySymbols()`.
- `getPrototypeOf(target)` - Intercepts calls to `Object.getPrototypeOf()`.
- `setPrototypeOf(target, prototype)` - Intercepts calls to `Object.setPrototypeOf()`.
- `isExtensible(target)` - Intercepts calls to `Object.isExtensible()`.
- `preventExtensions(target)` - Intercepts calls to `Object.preventExtensions()`.
There are many common and novel use-cases for the `Proxy` object, utilizing one or more of these traps. Here's a simple example that intercepts a `get` call and returns `null` if the property doesn't exist in the target object:
```js
const targetObj = { name: 'John', age: 30 };
const handler = {
get(target, property) {
return property in target ? target[property] : null;
}
};
const proxyObj = new Proxy(targetObj, handler);
proxyObj.name; // 'John'
proxyObj.age; // 30
proxyObj.address; // null
```

View File

@ -0,0 +1,21 @@
---
title: "Tip: Remove duplicates from a JavaScript array"
shortTitle: Remove duplicates from an array
type: tip
language: javascript
tags: [array]
author: chalarangelo
cover: architectural
excerpt: Easily remove duplicates from a JavaScript array using the built-in `Set` object.
dateModified: 2021-06-12T19:30:41+03:00
---
Removing duplicates from an array in JavaScript can be done in a variety of ways, such as using `Array.prototype.reduce()`, `Array.prototype.filter()` or even a simple `for` loop. But there's an easier alternative. JavaScript's built-in [`Set`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) object is described as a collection of values, where each value may occur only once. A `Set` object is also iterable, making it easily convertible to an array using the spread (`...`) operator.
```js
const nums = [1, 2, 2, 3, 1, 2, 4, 5, 4, 2, 6];
[...new Set(nums)] // [1, 2, 3, 4, 5, 6]
```
You can wrap this in a helper method, which is exactly what the [uniqueElements](/js/s/unique-elements) snippet does. For more complex cases, such as unique objects in an array based on a specific key, you might want to take a look at [uniqueElementsBy](/js/s/unique-elements-by).

View File

@ -0,0 +1,44 @@
---
title: Remove an element from a JavaScript array
shortTitle: Remove element from array
type: story
language: javascript
tags: [array]
author: chalarangelo
cover: maple-leaf-palette
excerpt: Did you know there are multiple ways to remove an element from an array? Let's take a look.
dateModified: 2022-06-26T05:00:00-04:00
---
Removing a specific value or values from an array in JavaScript is a pretty common task. While not hard to accomplish, there are a few approaches available, each with their own pros and cons. Choosing the correct one ultimately depends on the use case.
### Array.prototype.splice()
This is probably the most common approach for removing elements from an array. While `Array.prototype.splice()` is a very versatile tool in your kit, you need to remember that it **mutates the original array**. On top of that, it returns the deleted elements instead of a new array.
```js
const arr = ['a', 'b', 'c'];
const deleted = arr.splice(1, 1); // ['b']
console.log(arr); // ['a', 'c']
```
If you're cool with mutating the array, `Array.prototype.splice()` might just be the solution you need.
### Array.prototype.filter()
Another option for removing elements from an array is `Array.prototype.filter()`. More versatile than `Array.prototype.splice()`, it **doesn't mutate the original array**, but instead returns a new one. However, there's a performance consideration to be made for larger arrays when the elements to be removed are only encountered once. `Array.prototype.filter()` will always iterate over all elements in the array, which might be impractical in some cases.
```js
const arr = ['a', 'b', 'c'];
const filtered = arr.filter(el => el !== 'b'); // ['a', 'c']
console.log(arr); // ['a', 'b', 'c']
```
Most of the time, `Array.prototype.filter()` is the best option for removing elements from an array.
### Alternative options
The previous two options should cover the vast majority of use cases. Yet, there are some other options available for removing elements from an array, which might be preferable in certain cases. For example, if you like the interface of `Array.prototype.splice()` but need immutability, the [shank snippet](/js/s/shank) might be the solution for you. Similarly, when working with large unsorted arrays, there's a [fast removal trick](/js/s/fast-remove-array-element) that might be of interest to you.

View File

@ -0,0 +1,51 @@
---
title: How can I remove trailing zeros from a number in JavaScript?
shortTitle: Remove trailing zeros from number
type: story
language: javascript
tags: [math]
author: chalarangelo
cover: island-corridor
excerpt: When formatting decimal values in JavaScript, trailing zeros can be undesired. Here's how to deal with them.
dateModified: 2022-05-08T05:00:00-04:00
---
JavaScript provides a couple of ways to format numeric values to a given precision. Namely, you can use `Number.prototype.toFixed()` and `Number.prototype.toPrecision()` to similar effect. However, neither of them deals with trailing zeros in the decimal part. Here's a few ways you can remove them:
### Regular expression
Provided that the number is converted to a fixed-point string, you can use a regular expression to remove trailing zeros. All you have to do is match the decimal point (`\.`) and replace any zeros after it (`0+`) until the end of the string (`$`).
```js
const toFixedWithoutZeros = (num, precision) =>
num.toFixed(precision).replace(/\.0+$/, '');
toFixedWithoutZeros(1.001, 2); // '1'
toFixedWithoutZeros(1.500, 2); // '1.50'
```
The main issue with this approach is that the regular expression will only remove trailing zeros if the decimal part has no other digits before them. Writing a regular expression to remove trailing zeros from any number is a bit more involved and gets harder to read. Thus, this approach is discouraged.
### Multiply by 1
A better way to remove trailing zeros is to multiply by `1`. This method will remove trailing zeros from the decimal part of the number, accounting for non-zero digits after the decimal point. The only downside is that the result is a numeric value, so it has to be converted back to a string.
```js
const toFixedWithoutZeros = (num, precision) =>
`${1 * num.toFixed(precision)}`;
toFixedWithoutZeros(1.001, 2); // '1'
toFixedWithoutZeros(1.500, 2); // '1.5'
```
### Number.parseFloat
Similar to the previous approach, you can use `Number.parseFloat()` to remove trailing zeros from a number. This method also accounts for non-zero digits after the decimal point. We recommend this approach as it's the most readable.
```js
const toFixedWithoutZeros = (num, precision) =>
`${Number.parseFloat(num.toFixed(precision))}`;
toFixedWithoutZeros(1.001, 2); // '1'
toFixedWithoutZeros(1.500, 2); // '1.5'
```

View File

@ -0,0 +1,39 @@
---
title: Replace all occurrences of a string in JavaScript
shortTitle: Replace all occurrences of a string
type: story
language: javascript
tags: [string,regexp]
author: chalarangelo
cover: blue-computer
excerpt: If you need to replace all occurrences of a string in JavaScript, you have a couple of options.
dateModified: 2022-07-03T05:00:00-04:00
---
### String.prototype.replaceAll()
Modern JavaScript engines have a built-in method called `String.prototype.replaceAll()`. This method can be used to replace all occurrences of a string in another string with relative ease.
```js
const str = 'Hello World';
str.replaceAll('o', 'x'); // 'Hellx Wxrld'
```
Using `String.prototype.replaceAll()` is the recommended approach, as it's straightforward. If, however, you need to support older browsers, consider the option below.
### String.prototype.replace()
Before the introduction of `String.prototype.replaceAll()`, `String.prototype.replace()` was the method of choice for this sort of task. It's supported by all JavaScript engines, old and new and is very similar to `String.prototype.replaceAll()`.
While this method doesn't replace all occurrences of a string, it supports regular expressions. Knowing the string to be replaced, a regular expression can be created with the global (`'g'`) flag. Then, it can be passed to `String.prototype.replace()` to replace all occurrences of the string. The only issue here is that special characters need to be escaped, so that they are matched correctly. The [escapeRegExp snippet](/js/s/escape-reg-exp) comes in handy for this task.
```js
const escapeRegExp = str => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const replaceAll = (str, subStr, newSubStr) =>
str.replace(new RegExp(escapeRegExp(subStr), 'g'), newSubStr);
const str = 'Hello World';
replaceAll(str, 'o', 'x'); // 'Hellx Wxrld'
```

View File

@ -0,0 +1,42 @@
---
title: 3 ways to use the JavaScript spread operator with arrays
shortTitle: Array tricks using the spread operator
type: story
language: javascript
tags: [array]
author: chalarangelo
cover: succulent-crowd
excerpt: JavaScripts spread operator is a very versatile tool. Here are some simple ways to use it.
dateModified: 2022-04-17T05:00:00-04:00
---
### Clone an array
The spread operator can be used to clone an array into a new array. This trick can come in handy when working with arrays of primitives. However, it only shallow clones the array, meaning nested non-primitive values will not be cloned.
```js
const arr = [1, 2, 3];
const arr2 = [...arr];
// [1, 2, 3]
```
### Merge multiple arrays
Using the spread operator, its possible to combine two or more arrays into one. You should think of this trick as cloning two arrays into a new one. Due to that, the shallow cloning limitation mentioned previously applies here, too.
```js
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combined = [...arr1, ...arr2];
// [1, 2, 3, 4, 5, 6]
```
### Add items to an array
Similarly to previous tricks, its possible to spread an array into a new one and add individual elements, too. This can also be combined with merging multiple arrays, if desired.
```js
const arr = [1, 2, 3];
const arr2 = [0, ...arr, 4];
// [0, 1, 2, 3, 4]
```

View File

@ -0,0 +1,24 @@
---
title: What is the difference between static and instance methods?
shortTitle: Static Instance Methods
type: question
language: javascript
tags: [object,function,class]
author: chalarangelo
cover: lake-runner
excerpt: Static and instance methods are pretty easy to distinguish and serve different purposes. Learn all about them in this article.
dateModified: 2022-01-30T05:00:00-04:00
---
Static methods belong to a class and dont act on its instances. This means that they cant be called on instances of the class. Instead, they're called on the class itself. They are often utility functions, such as functions to create or clone objects.
Instance methods belong to the class prototype, which is inherited by all instances of the class. As such, they act on class instances and can be called on them.
```jsx
const arr = [1, 2, 3]; // An Array instance
Array.isArray(arr); // Static method of Array
arr.push(4); // Instance method of Array
```
In the context of ES6 classes, the `static` keyword is used to define static methods for a class. Conversely, methods not defined as `static` are instance methods.

View File

@ -0,0 +1,98 @@
---
title: How are HTMLElement.innerText and Node.textContent different?
shortTitle: Differences between innerText and textContent
type: question
language: javascript
tags: [browser]
author: chalarangelo
cover: dark-city
excerpt: While these two properties are very similar, there are some key differences that you should be aware of.
dateModified: 2023-03-19T05:00:00-04:00
---
JavaScript provides two properties you can use to **access the text content of an element**: `Node.textContent` and `HTMLElement.innerText`. For the most part, these two appear to be interchangeable. In fact, many developers use them interchangeably, not knowing that there are important differences between the two.
### Similarities
I think it's helpful to identify the similarities of these two properties before diving into the differences. This will also clarify how they're used in most cases.
Suppose you have an HTML element, containing some text:
```html
<p id="greeting">Hi there! My name is <strong>Bubbles</strong>.</p>
```
Both of these properties will return the text content of the element, including the text content of any **child elements**. They will also **ignore any HTML tags** that may be present in the element's content. And, they can be used to **set the text content** of the element, too.
```js
const greeting = document.getElementById('greeting');
greeting.innerText; // "Hi there! My name is Bubbles."
greeting.textContent; // "Hi there! My name is Bubbles."
greeting.innerText = 'Hello!'; // <p id="greeting">Hello!</p>
greeting.textContent = 'Hi!'; // <p id="greeting">Hi!</p>
```
### Differences
So far, these two properties appear to do the exact same thing. In fact, they both offer some convenient features that make them very useful. However, they start to exhibit some differences when the element's content is a little more complex.
Take the following HTML element, for example:
```html
<div class="card">
<style>
p { color: red; }
strong { text-transform: uppercase; }
small { display: none; }
</style>
<p>Hi there!<br />My name is <strong>Bubbles</strong>.</p>
<small>And I'm a <strong>dog</strong>.</small>
</div>
```
Let's take a look at the output of each of these two properties and see how they differ.
```js
const card = document.querySelector('.card');
card.innerText;
/*
"Hi there!
My name is BUBBLES."
*/
card.textContent;
/*
"
p { color: red; }
strong { text-transform: uppercase; }
small { display: none; }
Hi there!My name is Bubbles.
And I'm a dog.
"
*/
```
It's drastically different in this case, right? `HTMLElement.innerText` is supposed to roughly **match what the user sees** in the browser. Another way to think of this is that its output should closely resemble what the user would get if they were to select the element's content and copy it to their clipboard.
The first thing to notice, based on this definition, is that **hidden elements are ignored**. This applies to elements that don't render, such as `<style>` and `<script>`, but also to elements that are hidden using CSS. In this example, the `<small>` element is hidden, so it's not included in the output of `HTMLElement.innerText`.
Secondly, the output of `HTMLElement.innerText` is **normalized**. This means that all whitespace is collapsed into a single space, and all line breaks are replaced with a single line break. If present, `<br>` tags are also respected, so they're replaced with a line break.
The final point I want to make is that `HTMLElement.innerText` applies **text transformations** to the element's content. In this case, the `<strong>` element is transformed to uppercase, so the output of `HTMLElement.innerText` reflects this.
On the other hand, `Node.textContent` returns the **exact text content** of the element, including any whitespace and line breaks. Yet, `<br>` tags are stripped without any sort of replacement. It also includes the text content of any hidden elements, such as `<style>` and `<script>` and no text transformations are applied.
### Performance
But, wait! There's more! While `HTMLElement.innerText` seems like the sensible choice, it comes with a performance caveat. In order to figure out what the browser renders, CSS has to be considered, triggering a [reflow](https://developer.mozilla.org/en-US/docs/Glossary/Reflow). This can be **computationally expensive**, and can create inadvertent performance bottlenecks.
In my opinion, a good rule of thumb is to prefer `Node.textContent` for plain text elements, if possible. For more complex elements, try identifying how they're affected by layout and user interactions. For example, a complex element that's rendered only once and never altered, would be a use case for `HTMLElement.innerText`, but you can store the output in a variable and reuse it.
### Conclusion
`HTMLElement.innerText` and `Node.textContent` are two very similar properties that can be used to access and manipulate the text content of an element. However, they differ in some important ways, and you should be aware of these differences to choose the one that best suits your needs. Always examine your use case and consider the performance implications of your choice.

View File

@ -0,0 +1,27 @@
---
title: How delays work in JavaScript timing functions
shortTitle: Delays in JavaScript timing functions
type: story
language: javascript
tags: [browser,timeout]
author: chalarangelo
cover: river-house-lights
excerpt: Did you know that the delay of `setTimeout()` and `setInterval()` is merely a suggestion?
dateModified: 2022-10-26T05:00:00-04:00
---
JavaScript provides two widely used timing functions, `setTimeout()` and `setInterval()`. Both of these functions execute a specified function after a given amount of time either once or repeatedly. While this is straightforward, many people don't realize that **the delay is merely a suggestion** and can be altered by a number of factors.
### Delays are not exact
I went into detail about how JavaScript engines execute code in [the Event Loop explanation](/js/s/event-loop-explained), but let me recap here. As JavaScript is single-threaded, tasks are queued to be executed in a loop. Thus, `setTimeout()` and `setInterval()` are tasks that will be executed after at least the given amount of time has elapsed. There is no guarantee, however, that the task will be executed exactly after the given amount of time has elapsed. The delay is a suggestion, signifying the **minimum amount of time** that must pass before the task is executed. The actual delay can be longer, depending on the current state of the JavaScript engine.
### Browser factors
Apart from engine-related delays, there are a few other factors that play a role in the actual delay of a task. Briefly, these are:
- Browsers **throttle nested timeouts and intervals** with a delay of at least 4ms, but this can vary depending on the browser.
- Timeouts and intervals in **inactive/background tabs are throttled** to a minimum of 1000ms to increase battery life.
- **Known tracking scripts in background tabs** can be throttled even further after a certain amount of time.
On a side note, some browsers store delays as a 32-bit signed integer, meaning that delays over 24.8 days will cause an overflow and execute immediately.

View File

@ -0,0 +1,32 @@
---
title: "Tip: Typechecking arrays with Array.isArray()"
shortTitle: Array typechecking
type: tip
language: javascript
tags: [type,array]
author: chalarangelo
cover: purple-flower-field
excerpt: Make sure to use the correct method when checking if a JavaScript object is an array.
dateModified: 2022-11-06T05:00:00-04:00
---
To determine if a JavaScript object is an array, you can either use `Array.isArray()` or the `instanceof` operator. While both methods work for arrays created either using the array literal syntax or the `Array` constructor, there's a key difference. `Array.isArray()` is more reliable, as it works with cross-realm-objects, such as those created in an `iframe`.
```js
var iframeEl = document.createElement('iframe');
document.body.appendChild(iframeEl);
iframeArray = window.frames[window.frames.length - 1].Array;
var array1 = new Array(1,1,1,1);
var array2 = new iframeArray(1,1,1,1);
console.log(array1 instanceof Array); // true
console.log(Array.isArray(array1)); // true
console.log(array2 instanceof Array); // false
console.log(Array.isArray(array2)); // true
```
As illustrated in the previous example, `instanceof` breaks when working with an `iframe`. However, `Array.isArray()` produces the correct result regardless of the way the array was instantiated.
If you are interested in knowing why `instanceof Array` doesn't work across different globals (i.e. `iframe` or `window`), you can read more about it [here](http://web.mit.edu/jwalden/www/isArray.html).

View File

@ -0,0 +1,94 @@
---
title: Typechecking objects with Proxy in JavaScript
shortTitle: Object typechecking with Proxy
type: story
language: javascript
tags: [object,type,proxy]
author: chalarangelo
cover: customs
excerpt: A simple way to typecheck objects at runtime using the Proxy object.
dateModified: 2023-04-23T05:00:00-04:00
---
A while back, I was working on a project where some objects had **rigid structure requirements**. As I was really not in the mood to use TypeScript, I decided to create a typechecking mechanism for objects using the `Proxy` object.
Drawing inspiration from React's `PropTypes`, I created a handful of **type checking functions** for the most common types.
```js
const bool = v => typeof v === 'boolean';
const num = v => typeof v === 'number' && v === v;
const str = v => typeof v === 'string';
const date = v => v instanceof Date;
```
The next step was to decide on how an **object's shape** would be defined. This proved an easy task, as I could simply use the names of the type checking functions as values for the keys of the object.
```js
const shape = { name: 'str', age: 'num', active: 'bool', birthday: 'date' };
```
Having decided how to define shapes, I needed to convert this shape definition into a function that would take an object and wrap it with a `Proxy`. The `Proxy` would in turn **intercept any attempts to set a property** and check if the value being set is of the correct type. If it is, the value is set as expected. If not, the trap returns `false`, which means the operation was not a success. Similarly, properties not in the shape definition should not be set, so the trap returns `false` for those as well.
```js
const createShapeCheckerProxy = (types, shape) => {
const validProps = Object.keys(shape);
const handler = {
set(target, prop, value) {
if (!validProps.includes(prop)) return false;
const validator = types[shape[prop]];
if (!validator || typeof validator !== 'function') return false;
if (!validator(value)) return false;
target[prop] = value;
}
};
return obj => new Proxy(obj, handler);
};
```
Having set everything up, it was time to test it out. Here's an example of the whole thing put together:
```js
const createShapeCheckerProxy = shape => {
const types = {
bool: v => typeof v === 'boolean',
num: v => typeof v === 'number' && v === v,
str: v => typeof v === 'string',
date: v => v instanceof Date
};
const validProps = Object.keys(shape);
const handler = {
set(target, prop, value) {
if (!validProps.includes(prop)) return false;
const validator = types[shape[prop]];
if (!validator || typeof validator !== 'function') return false;
if (!validator(value)) return false;
target[prop] = value;
}
};
return obj => new Proxy(obj, handler);
};
const shapeCheckerProxy = createShapeCheckerProxy({
name: 'str', age: 'num', active: 'bool', birthday: 'date'
});
const obj = {};
const proxiedObj = shapeCheckerProxy(obj);
// These are valid
proxiedObj.name = 'John';
proxiedObj.age = 34;
proxiedObj.active = false;
proxiedObj.birthday = new Date('1989-04-01');
// These will fail
proxiedObj.name = 404;
proxiedObj.age = false;
proxiedObj.active = 'no';
proxiedObj.birthday = null;
proxiedObj.whatever = 'something';
```
As you can see, `createShapeCheckerProxy` can be used with a plain object to create a reusable function that wraps an object with a typechecking `Proxy`. The defined `types` are used to typecheck individual properties and could be extended to support more complex types and special rules. Overall, this can be a pretty useful tool for **typechecking objects at runtime**, without having to use TypeScript or similar tools.

View File

@ -0,0 +1,63 @@
---
title: Window.location Cheat Sheet
type: cheatsheet
language: javascript
tags: [browser]
author: chalarangelo
cover: yellow-sofa
excerpt: A quick reference for the `window.location` object.
dateModified: 2022-12-21T05:00:00-04:00
---
The `window.location` object is particularly useful when working with a page's URL information. Let's take a look at an example of a URL and what each property of the `window.location` object represents.
```js
const url = 'https://dev.30secondsofcode.org:8000/c/js?page=2&sort=asc#search';
```
Provide the above URL, here's a quick reference for the properties `window.location` object:
### window.location.protocol
- The protocol schema of the URL (usually `http:` or `https:`)
- Sample value: `https:`
### window.location.hostname
- The domain name of the URL
- Sample value: `dev.30secondsofcode.org`
### window.location.port
- The port number of the URL
- Sample value: `8000`
### window.location.host
- The domain name and port number of the URL
- Sample value: `dev.30secondsofcode.org:8000`
### window.location.origin
- The protocol schema, domain name and port number of the URL
- Sample value: `https://dev.30secondsofcode.org:8000`
### window.location.pathname
- The path of the URL, including the leading slash
- Sample value: `/c/js`
### window.location.search
- The query string of the URL, including the leading question mark
- Sample value: `?page=2&sort=asc`
### window.location.hash
- The fragment identifier of the URL, including the leading hash
- Sample value: `#search`
### window.location.href
- The full URL, including the protocol schema, domain name, port number, path, query string and fragment identifier
- Sample value: `https://dev.30secondsofcode.org:8000/c/js?page=2&sort=asc#search`