Rename language articles

This commit is contained in:
Angelos Chalaris
2023-05-18 23:57:15 +03:00
parent 45d1fa023a
commit 2d58c1dfd7
48 changed files with 17 additions and 17 deletions

View File

@ -0,0 +1,54 @@
---
title: 10 must-have VS Code extensions for JavaScript developers
shortTitle: Essential VS Code extensions
type: story
language: javascript
tags: [devtools,vscode]
author: chalarangelo
cover: computer-screens
excerpt: Boost your productivity with these 10 essential VS Code extensions for JavaScript developers.
unlisted: true
dateModified: 2021-06-12T19:30:41+03:00
---
Developers will most likely argue for the rest of eternity about the most productive code editor and the best extensions. Here are my personal extension preferences for VS Code as a JavaScript developer:
### ESLint
[ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) turns the popular JavaScript linter into an extension of VS Code. It automatically reads your linting configuration, identifies problems and even fixes them for you, if you want.
### GitLens
[GitLens](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens) is a very powerful collaboration tool for VS Code. It provides many useful tools for git such as blame, code authorship, activity heatmaps, recent changes, file history and even commit search.
### Debugger for Chrome
[Debugger for Chrome](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome) allows you to debug your JavaScript code in Chrome or Chromium. Breakpoints, call stack inspection and stepping inside a function are only some of its features.
### Bracket Pair Colorizer 2
[Bracket Pair Colorizer 2](https://marketplace.visualstudio.com/items?itemName=CoenraadS.bracket-pair-colorizer-2) makes reading code faster as it makes matching brackets the same color. This extension for VS Code improves upon its predecessor by providing improved performance.
### Bookmarks
[Bookmarks](https://marketplace.visualstudio.com/items?itemName=alefragnani.Bookmarks) is one of those extensions that will significantly reduce your time jumping between different files, as it allows you to save important positions and navigate back to them easily and quickly.
### TODO Highlight
[TODO Highlight](https://marketplace.visualstudio.com/items?itemName=wayou.vscode-todo-highlight) simplifies tracking leftover tasks by allowing you to list all of your TODO annotations, as well as adding a handy background highlight to them to make them pop out immediately.
### Live Server
[Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer) gives you an easy way to serve web pages from VS Code, making previewing and debugging a lot easier. One of the core features is the live reload support that many developers are used to.
### REST Client
[REST Client](https://marketplace.visualstudio.com/items?itemName=humao.rest-client) allows you to send HTTP requests and view the responses directly in VS Code. This extension supports a wide range of formats and authorization and should work with most setups.
### One Dark Pro
[One Dark Pro](https://marketplace.visualstudio.com/items?itemName=zhuangtongfa.Material-theme) is one of the most popular VS Code themes and with very good reason. It provides a clean theme with a nice palette that has great contrast and is very comfortable to use on a daily basis.
### Fira Code
[Fira Code](https://github.com/tonsky/FiraCode) is not a traditional VS Code extension and might take a couple more steps to set up, but it's a superb programming font with ligatures that will help you scan code faster once you get used to it.

View File

@ -0,0 +1,59 @@
---
title: 4 JavaScript Array methods you must know
shortTitle: Useful array methods
type: story
language: javascript
tags: [array,cheatsheet]
author: chalarangelo
cover: arrays
excerpt: JavaScript arrays have a very robust API offering some amazing tools. Learn the 4 must-know JavaScript array methods in this quick guide.
dateModified: 2021-06-12T19:30:41+03:00
---
JavaScript arrays have a very robust API offering a plethora of amazing tools. Here are our top 4 JavaScript array methods every developer should know:
### Array.prototype.map()
[`Array.prototype.map()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) creates a new array by applying the provided transformation to each element of the original array. The result is an array with the same length as the original array and elements transformed based on the provided function.
```js
const arr = [1, 2, 3];
const double = x => x * 2;
arr.map(double); // [2, 4, 6]
```
### Array.prototype.filter()
[`Array.prototype.filter()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) creates a new array by using a filtering function to keep only elements that return `true` based on that function. The result is an array with equal or less than the original array's length, containing a subset of the same elements as the original array.
```js
const arr = [1, 2, 3];
const isOdd = x => x % 2 === 1;
arr.filter(isOdd); // [1, 3]
```
![JavaScript Array Methods](./illustrations/js-array-methods.png)
### Array.prototype.reduce()
[`Array.prototype.reduce()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce) creates an output value of any type depending on a reducer function and an initial value. The result can be of any type such as an integer, an object or an array, based on the reducer function provided.
```js
const arr = [1, 2, 3];
const sum = (x, y) => x + y;
arr.reduce(sum, 0); // 6
const increment = (x, y) => [...x, x[x.length - 1] + y];
arr.reduce(increment, [0]); // [0, 1, 3, 6]
```
### Array.prototype.find()
[`Array.prototype.find()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find) returns the first element for which a matcher function returns `true`. The result is a single element from the original array.
```js
const arr = [1, 2, 3];
const isOdd = x => x % 2 === 1;
arr.find(isOdd); // 1
```

View File

@ -0,0 +1,121 @@
---
title: 6 JavaScript Regular Expression features you can use today
shortTitle: JavaScript Regular Expression tips
type: story
language: javascript
tags: [string,regexp]
author: chalarangelo
cover: taking-photos
excerpt: Regular expressions are very powerful, but hard to master. Understand these features and start using them in your JavaScript code.
dateModified: 2021-06-12T19:30:41+03:00
---
Regular expressions, while very powerful, are notoriously hard to master. Here are 6 useful features that can help you start using them in your JavaScript projects:
### Capturing groups
Capturing groups allow you to get specific parts of the matched string, simply by wrapping part of the regular expression in parentheses `(...)`:
```js
const str = 'JavaScript is a programming language';
/(JavaScript) is a (.*)/.exec(str);
/*
[
0: 'JavaScript is a programming language',
1: 'JavaScript',
2: 'programming language'
]
*/
```
### Non-capturing groups
Non-capturing groups are used for matching something without capturing it, like an either/or matching group that you do not really need. They are defined similarly to capturing groups, but prefixed with `?:`:
```js
const str = 'JavaScript is a programming language';
/(?:JavaScript|Python) is a (.+)/.exec(str);
/*
[
0: 'JavaScript is a programming language',
1: 'programming language'
]
*/
```
### Named capturing groups
Named capturing groups allow you to name a capturing group, by prefixing it with `<name>`:
```js
const str = 'JavaScript is a programming language';
/(?<subject>.+) is a (?<description>.+)/.exec(str);
/*
[
0: 'JavaScript is a programming language',
1: 'JavaScript',
2: 'programming language',
groups: {
subject: 'JavaScript,
description: 'programming language'
}
]
*/
```
### Capturing group backreferences
Backreferences help you write shorter regular expressions, by repeating an existing capturing group, using `\1`, `\2` etc. Similarly, you can also repeat named capturing groups using `\k<name>`:
```js
const str = 'JavaScript is a programming language - an awesome programming language JavaScript is';
/(.+) is a (?<description>.+) - an awesome \k<description> \1 is/.exec(str);
/*
[
0: 'JavaScript is a programming language - an awesome programming language JavaScript is',
1: 'JavaScript',
2: 'programming language',
groups: {
subject: 'JavaScript,
description: 'programming language'
}
]
*/
```
### Lookaheads
Lookaheads allow you to check if something is followed by a certain pattern, without actually matching it. You can create positive lookaheads using `?=` and negative lookaheads using `?!`:
```js
const str = 'JavaScript is not the same as Java and you should remember that';
/Java(?=Script)(.*)/.exec(str);
/*
[
0: 'JavaScript is not the same as Java and you should remember that',
1: 'Script is not the same as Java and you should remember that'
]
*/
/Java(?!Script)(.*)/.exec(str);
/*
[
0: 'Java and you should remember that',
1: ' and you should remember that'
]
*/
```
### Unicode characters
Finally, you can match unicode characters, using `/p{...}` and the `/u` flag. Examples include, but are not limited to `{Emoji}`, `{Math_Symbols}` and `{Script=Greek}`:
```js
const str = 'Greek looks like this: γεια';
/\p{Script=Greek}+/u.exec(str);
/*
[
0: 'γεια'
]
*/
```

View File

@ -0,0 +1,113 @@
---
title: Asynchronous JavaScript Cheat Sheet
type: cheatsheet
language: javascript
tags: [function,promise]
author: chalarangelo
cover: green-plant
excerpt: Learn everything you need to know about promises and asynchronous JavaScript with this handy cheatsheet.
dateModified: 2021-06-12T19:30:41+03:00
---
### Promise basics
- **Promises** start in a **pending state**, neither fulfilled or rejected.
- When the operation is completed, a promise will become **fulfilled with a value**.
- If the operation fails, a promise will get **rejected with an error**.
### Creating promises
- The function passed to the `Promise` constructor will execute synchronously.
- Use `resolve()` or `reject()` to create promises from values.
- `Promise.resolve(val)` will fulfill the promise with `val`.
- `Promise.reject(err)` will reject the promise with `err`.
- If you put a fulfilled promise into a fulfilled promise, they will collapse into one.
```js
// Resolving with a value, rejecting with an error
new Promise((resolve, reject) => {
performOperation((err, val) => {
if (err) reject(err);
else resolve(val);
});
});
// Resolving without value, no need for reject
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
```
### Handling promises
- `Promise.prototype.then()` accepts two optional arguments (`onFulfilled`, `onRejected`).
- `Promise.prototype.then()` will call `onFulfilled` once the promise is fulfilled.
- `Promise.prototype.then()` will call `onRejected` if the promise is rejected.
- `Promise.prototype.then()` passes errors through if `onRejected` in undefined.
- `Promise.prototype.catch()` accepts one argument (`onRejected`).
- `Promise.prototype.catch()` behaves like `Promise.prototype.then()` when `onFulfilled` is omitted.
- `Promise.prototype.catch()` passes fulfilled values through.
- `Promise.prototype.finally()` accepts one argument (`onFinally`).
- `Promise.prototype.finally()` calls `onFinally` with no arguments once any outcome is available.
- `Promise.prototype.finally()` passes through input promise.
```js
promisedOperation()
.then(
val => value + 1, // Called once the promise is fulfilled
err => { // Called if the promise is rejected
if (err === someKnownErr) return defaultVal;
else throw err;
}
)
.catch(
err => console.log(err); // Called if the promise is rejected
)
.finally(
() => console.log('Done'); // Called once any outcome is available
);
```
- All three of the above methods will not be executed at least until the next tick, even for promises that already have an outcome.
### Combining promises
- `Promise.all()` turns an array of promises into a promise of an array.
- If any promise is rejected, the error will pass through.
- `Promise.race()` passes through the first settled promise.
```js
Promise
.all([ p1, p2, p3 ])
.then(([ v1, v2, v3 ]) => {
// Values always correspond to the order of promises,
// not the order they resolved in (i.e. v1 corresponds to p1)
});
Promise
.race([ p1, p2, p3 ])
.then(val => {
// val will take the value of the first resolved promise
});
```
### async/await
- Calling an `async` function always results in a promise.
- `(async () => value)()` will resolve to `value`.
- `(async () => throw err)()` will reject with an error.
- `await` waits for a promise to be fulfilled and returns its value.
- `await` can only be used in `async` functions.
- `await` also accepts non-promise values.
- `await` always waits at least until the next tick before resolving, even when waiting already fulfilled promises or non-promise values.
```js
async () => {
try {
let val = await promisedValue();
// Do stuff here
} catch (err) {
// Handle error
}
}
```

View File

@ -0,0 +1,64 @@
---
title: Big-O Cheat Sheet
type: cheatsheet
language: javascript
tags: [algorithm]
author: chalarangelo
cover: light-ring
excerpt: Learn everything you need to know about Big-O notation with this handy cheatsheet.
dateModified: 2023-01-08T05:00:00-04:00
---
### Definition
Big-O notation, represents an algorithm's **worst-case complexity**. It uses algebraic terms to describe the complexity of an algorithm, allowing you to measure its efficiency and performance. Below you can find a chart that illustrates Big-O complexity:
![Big-O Complexity Chart](./illustrations/big-o-complexity.png)
Simply put, `O(1)` stands for **constant time complexity**, which is the most efficient, while `O(n!)` stands for **factorial time complexity**, which is the least efficient. The `n` in the complexity represents the size of the input, so `O(n)` means that the algorithm's time complexity will grow linearly with the size of the input.
Apart from Big-O notation, there are other notations that are used to describe the complexity of an algorithm, such as `Ω` (Omega) and `Θ` (Theta). `Ω` describes the **best-case complexity** of an algorithm, while `Θ` describes the **average-case complexity** of an algorithm.
### Common Data Structure operations
Different data structures have different time complexities for the same operations. For example, a linked list has `O(1)` time complexity for `insert` and `delete` operations, while an array has `O(n)` time complexity for the same operations. Below you can find average and worst time complexities for data structures used commonly in web development.
#### Average time complexity
| Data Structure | Access | Search | Insertion | Deletion |
| --- | --- | --- | --- | --- |
| [**Array**](/js/s/native-data-structures) | Θ(1) | Θ(n) | Θ(n) | Θ(n) |
| [**Queue**](/js/s/data-structures-queue) | Θ(n) | Θ(n) | Θ(1) | Θ(1) |
| [**Stack**](/js/s/data-structures-stack) | Θ(n) | Θ(n) | Θ(1) | Θ(1) |
| [**Linked List**](/js/s/data-structures-linked-list) | Θ(n) | Θ(n) | Θ(1) | Θ(1) |
| [**Doubly Linked List**](/js/s/data-structures-doubly-linked-list) | Θ(n) | Θ(n) | Θ(1) | Θ(1) |
| **Skip List** | Θ(log n) | Θ(log n) | Θ(log n) | Θ(log n) |
| **Hash Table** | N/A | Θ(1) | Θ(1) | Θ(1) |
| [**Binary Search Tree**](/js/s/data-structures-binary-search-tree) | Θ(log n) | Θ(log n) | Θ(log n) | Θ(log n) |
#### Worst time complexity
| Data Structure | Access | Search | Insertion | Deletion |
| --- | --- | --- | --- | --- |
| [**Array**](/js/s/native-data-structures) | O(1) | O(n) | O(n) | O(n) |
| [**Queue**](/js/s/data-structures-queue) | O(n) | O(n) | O(1) | O(1) |
| [**Stack**](/js/s/data-structures-stack) | O(n) | O(n) | O(1) | O(1) |
| [**Linked List**](/js/s/data-structures-linked-list) | O(n) | O(n) | O(1) | O(1) |
| [**Doubly Linked List**](/js/s/data-structures-doubly-linked-list) | O(n) | O(n) | O(1) | O(1) |
| **Skip List** | O(n) | O(n) | O(n) | O(n) |
| **Hash Table** | N/A | O(n) | O(n) | O(n) |
| [**Binary Search Tree**](/js/s/data-structures-binary-search-tree) | O(n) | O(n) | O(n) | O(n) |
### Array sorting algorithms
Similar to data structures, different array sorting algorithms have different time complexities. Below you can find the best, average and worst time complexities for the most common array sorting algorithms.
| Algorithm | Best | Average | Worst |
| --- | --- | --- | --- |
| [**Quick sort**](/js/s/quick-sort) | Ω(n log n) | Θ(n log n) | O(n^2) |
| [**Merge sort**](/js/s/merge-sort) | Ω(n log n) | Θ(n log n) | O(n log n) |
| [**Heap sort**](/js/s/heapsort) | Ω(n log n) | Θ(n log n) | O(n log n) |
| [**Bubble sort**](/js/s/bubble-sort) | Ω(n) | Θ(n^2) | O(n^2) |
| [**Insertion sort**](/js/s/insertion-sort) | Ω(n) | Θ(n^2) | O(n^2) |
| [**Selection sort**](/js/s/selection-sort) | Ω(n^2) | Θ(n^2) | O(n^2) |
| [**Bucket sort**](/js/s/bucket-sort) | Ω(n+k) | Θ(n+k) | O(n^2) |

View File

@ -0,0 +1,77 @@
---
title: Code Anatomy - For loops, array reduce and method chaining
shortTitle: For loops, array reduce and method chaining
type: story
language: javascript
tags: [array,iterator]
author: chalarangelo
cover: case-study
excerpt: There are many ways to iterate and transform array data in JavaScript. Learn how each one works and where you should use them.
dateModified: 2021-06-12T19:30:41+03:00
---
### For loops
```js
const files = [ 'foo.txt ', '.bar', ' ', 'baz.foo' ];
let filePaths = [];
for (let file of files) {
const fileName = file.trim();
if(fileName) {
const filePath = `~/cool_app/${fileName}`;
filePaths.push(filePath);
}
}
// filePaths = [ '~/cool_app/foo.txt', '~/cool_app/.bar', '~/cool_app/baz.foo']
```
- Any `for` loop can be used - [read more about the different JavaScript loops](/blog/s/javascript-for-in-for-of-foreach/).
- Less common nowadays, due to functional programming being more popular.
- Control over the iteration, such as skipping over elements or early `return`s.
- Resulting array needs to be declared beforehand, outside the loop.
- Uses `Array.prototype.push()` or the spread (`...`) operator to add elements.
- `O(N)` complexity, each element will be iterated over only once.
### Array reduce
```js
const files = [ 'foo.txt ', '.bar', ' ', 'baz.foo' ];
const filePaths = files.reduce((acc, file) => {
const fileName = file.trim();
if(fileName) {
const filePath = `~/cool_app/${fileName}`;
acc.push(filePath);
}
return acc;
}, []);
// filePaths = [ '~/cool_app/foo.txt', '~/cool_app/.bar', '~/cool_app/baz.foo']
```
- Uses `Array.prototype.reduce()` with an empty array as the initial value.
- More common nowadays, due to functional programming being more popular.
- Less control over the iteration, cannot skip elements or `return` early.
- Can be chained with other methods, if necessary.
- Uses `Array.prototype.push()` or the spread (`...`) operator to add elements.
- `O(N)` complexity, each element will be iterated over only once.
### Method chaining
```js
const files = [ 'foo.txt ', '.bar', ' ', 'baz.foo' ];
const filePaths = files
.map(file => file.trim())
.filter(Boolean)
.map(fileName => `~/cool_app/${fileName}`);
// filePaths = [ '~/cool_app/foo.txt', '~/cool_app/.bar', '~/cool_app/baz.foo']
```
- Uses `Array.prototype.map()` and `Array.prototype.filter()`.
- More common nowadays, due to functional programming being more popular.
- Less control over the iteration, cannot skip elements or `return` early.
- Declarative, easier to read and refactor, chain can grow as necessary.
- Does not use `Array.prototype.push()` or the spread (`...`) operator.
- `O(cN)` complexity, `c` iterations per element, (`c`: length of the chain).

View File

@ -0,0 +1,122 @@
---
title: Code Anatomy - Optimizing recursive functions
shortTitle: Optimizing recursive functions
type: story
language: javascript
tags: [recursion,performance]
author: chalarangelo
cover: case-study
excerpt: Recursive code tends to be inefficient or in need of optimization. Learn a couple of tricks we use to speed up our recursive functions.
dateModified: 2021-06-12T19:30:41+03:00
---
### Recursive functions
Recursion is a programming technique where the final solution is computed by breaking down the problem into smaller instances of the same problem and computing the solution for each one. The most common implementation is a function that calls itself, reducing the problem every time until it reaches an instance of the problem whose solution is either trivial to compute or already known. Let's look at a very well-known example, calculating the `n`th term of the [Fibonacci sequence](https://en.wikipedia.org/wiki/Fibonacci_number), implemented using recursion in JavaScript:
```js
const fibonacciNumber = n =>
n < 2 ? fibonacciNumber(n - 1) + fibonacciNumber(n - 2) : n;
```
To understand recursion better, let's add a `console.log()` call before each `return` and figure out what exactly is happening:
```js
const fibonacciNumber = n => {
console.log(`[CALLED] fibonacciNumber(${n})`);
const r = n >= 2 ? fibonacciNumber(n - 1) + fibonacciNumber(n - 2) : n;
console.log(`[RETURN] ${r} for n=${n}`);
return r;
}
fibonacciNumber(4);
// [CALLED] fibonacciNumber(4)
// [CALLED] fibonacciNumber(3)
// [CALLED] fibonacciNumber(2)
// [CALLED] fibonacciNumber(1)
// [RETURN] 1 for n=1
// [CALLED] fibonacciNumber(0)
// [RETURN] 0 for n=0
// [RETURN] 1 for n=2
// [CALLED] fibonacciNumber(1)
// [RETURN] 1 for n=1
// [RETURN] 2 for n=3
// [CALLED] fibonacciNumber(2)
// [CALLED] fibonacciNumber(1)
// [RETURN] 1 for n=1
// [CALLED] fibonacciNumber(0)
// [RETURN] 0 for n=0
// [RETURN] 1 for n=2
// [RETURN] 3 for n=4
```
As you can see, for each value of `n`, `fibonacciNumber` will be called twice, once with `n - 1` and once with `n - 2` and this will continue until it's called with either `1` or `0`. While this is straightforward to write and understand, it is inefficient as it will have to calculate the same value more than once.
### Calculation memoization
The solution to this problem, and the first trick that you can use to speed up recursive functions, is to use memoization. We already published [a great blog post on memoization](/js/s/memoization/) a little while back, so be sure to check it out to learn more about the subject. Here's our `fibonacciNumber` function, using memoization:
```js
const fibonacciCache = new Map();
const fibonacciNumber = n => {
console.log(`[CALL] fibonacciNumber(${n})`);
const cacheKey = `${n}`;
let r;
if(fibonacciCache.has(cacheKey)) {
r = fibonacciCache.get(cacheKey);
console.log(`[MEMO] Cache hit for ${n}: ${r}`);
}
else {
r = n >= 2 ? fibonacciNumber(n - 1) + fibonacciNumber(n - 2) : n;
fibonacciCache.set(cacheKey, r);
console.log(`[CALC] Computed and stored value for ${n}: ${r}`);
}
return r;
}
fibonacciNumber(4);
// [CALL] fibonacciNumber(4)
// [CALL] fibonacciNumber(3)
// [CALL] fibonacciNumber(2)
// [CALL] fibonacciNumber(1)
// [CALC] Computed and stored value for 1: 1
// [CALL] fibonacciNumber(0)
// [CALC] Computed and stored value for 0: 0
// [CALC] Computed and stored value for 2: 1
// [CALL] fibonacciNumber(1)
// [MEMO] Cache hit for 1: 1
// [CALC] Computed and stored value for 3: 2
// [CALL] fibonacciNumber(2)
// [MEMO] Cache hit for 2: 1
// [CALC] Computed and stored value for 4: 3
```
As you can see in the example above, the value for each `n` is only computed once. While the Fibonacci sequence doesn't require any costly calculations, this could make a huge difference for a more computationally expensive problem. It will also be a lot more noticeable for higher values of `n` where the number of calculations will increase significantly.
### Using iteration
The second and final trick stems from the very definition of recursive programming turned on its head. If we can solve a smaller instance of the problem and use it for the solution of a larger instance of the problem, it should be possible to work iteratively from the smaller problem to the larger one, instead of recursively. Here's this idea in practice for our `fibonacciNumber` function:
```js
const fibonacciNumber = n => {
let r = 0, l = 1, s = 0;
for(let i = 0; i < n; i++) {
r = l;
l = s;
s = r + l;
console.log(`[CALC] i = ${i}: r = ${r}, l = ${l}, s = ${s}`);
}
return s;
}
fibonacciNumber(4);
// [CALC] i = 0: r = 1, l = 0, s = 1
// [CALC] i = 1: r = 0, l = 1, s = 1
// [CALC] i = 2: r = 1, l = 1, s = 2
// [CALC] i = 3: r = 1, l = 2, s = 3
```
The iterative solution above makes the same calculations as the memoized one, however it performs better due to two key reasons. First of all, there is no cache, which would take up space in memory, making the latter implementation require fewer resources. Similarly, as there are no recursive calls or checks for cache hits, the code performs better and requires fewer resources to execute.
However, you have to bear in mind what the actual use cases of your recursive code are and be very careful how you optimize them. Memoization can be a more powerful tool if a recursive function is called multiple times with different arguments, as its cache persists between calls, while iteration can be faster for recursive computations that are used less frequently. Always pay attention to your code and optimize for the cases you know or anticipate to be more common.

View File

@ -0,0 +1,133 @@
---
title: Common regular expressions
type: cheatsheet
language: javascript
tags: [string,regexp]
author: chalarangelo
cover: rocky-beach
excerpt: A collection of regular expressions that can be used to solve common problems.
dateModified: 2022-11-09T05:00:00-04:00
---
### Exact string match
- Use the `^` and `$` anchors to match the start and end of the string, respectively.
- Add the string you want to match in-between the two anchors.
```js
const regexp = /^abc$/;
// Where 'abc' is the exact string you want to match
```
### Match empty string
- Use the `^` and `$` anchors to match the start and end of the string, respectively.
- Do not add any characters in-between to match an empty string.
```js
const regexp = /^$/;
```
### Match whitespace sequences
- Use the `\s` meta-sequence to match any whitespace character, including spaces, tabs, newlines, etc.
- Use the `+` quantifier to match one or more occurrences of the previous character.
- Add the global flag (`g`) to match all occurrences of the pattern in the string.
```js
const regexp = /\s+/g;
```
### Match line breaks
- Depending on the environment, line breaks can be represented in different ways.
- Use the `\r` character to match carriage returns, the `\n` character to match newlines, and the `\r\n` sequence to match carriage returns followed by newlines.
- Add the global (`g`) and multiline (`m`) flags to match all occurrences of the pattern in the string.
```js
const regexp = /\r|\n|\r\n/gm;
```
### Match non-word characters
- Use negation (`^`) to match any character that is not a word character (`\w`) or a whitespace character (`\s`).
- Add the global flag (`g`) to match all occurrences of the pattern in the string.
- Add the ignore case flag (`i`) to match both uppercase and lowercase characters.
```js
const regexp = /[^\w\s]/gi;
```
### Match alphanumeric, dashes and hyphens
- Use the `^` and `$` anchors to match the start and end of the string, respectively.
- Use the `a-zA-Z0-9-` pattern to match any alphanumeric character, dashes and hyphens.
- Use the `+` quantifier to match one or more occurrences of the previous character.
- Particularly useful when matching URL slugs.
```js
const regexp = /^[a-zA-Z0-9-_]+$/;
```
### Match letters and whitespaces
- Use the `^` and `$` anchors to match the start and end of the string, respectively.
- Use the `a-zA-Z\s` pattern to match any letter and whitespace character.
- Use the `+` quantifier to match one or more occurrences of the previous pattern.
```js
const regexp = /^[A-Za-z\s]+$/;
```
### Pattern not included
- Use the `^` and `$` anchors to match the start and end of the string, respectively.
- Use a negative lookahead (`?!`) to match any character that is not followed by the pattern you want to exclude.
- Add the global flag (`g`) to match all occurrences of the pattern in the string.
- To ensure more than one pattern is not included, use the `|` character to separate them.
```js
const regexp = /^((?!(abc|bcd)).)*$/;
// Where 'abc' and 'bcd' are pattern you want to exclude
```
### Text inside brackets
- Use the `\(` and `\)` characters to match the opening and closing brackets, respectively.
- Use a capturing group between the two and exclude the closing parenthesis character.
- Use the `+` quantifier to match one or more characters, as needed.
- Add the global flag (`g`) to match all occurrences of the pattern in the string.
- Replace `\(` and `\)` with `\[` and `\]` to match square brackets and with `\{` and `\}` to match curly brackets.
```js
const regexp = /\(([^)]+)\)/g;
```
### Validate GUID/UUID
- Use the `^` and `$` anchors to match the start and end of the string, respectively.
- Validate each segment of the GUID/UUID separately using numeric character ranges and quantifiers.
```js
const regexp = /^(0?[1-9]|[12][0-9]|3[01])[\/\-](0?[1-9]|1[012])[\/\-]\d{4}$/;
```
### Validate date format (DD/MM/YYYY)
- Use the `^` and `$` anchors to match the start and end of the string, respectively.
- Validate each segment of the date separately using numeric character ranges and quantifiers.
- Alter the order of the segments and separators to match different formats.
```js
const regexp = /^(0?[1-9]|[12][0-9]|3[01])[\/\-](0?[1-9]|1[012])[\/\-]\d{4}$/;
```
### Chunk string into n-size chunks
- Use the `.{1,n}` quantifier to match any character between `1` and `n` times.
- Add the global flag (`g`) to match all occurrences of the pattern in the string.
```js
const regexp = /.{1,2}/g;
// Where '2' is the number of characters per chunk
```

View File

@ -0,0 +1,119 @@
---
title: JavaScript console.log() tips & tricks
type: story
language: javascript
tags: [browser,cheatsheet]
author: chalarangelo
cover: terminal
excerpt: Level up your JavaScript logging with these `console.log()` tips and tricks.
dateModified: 2021-06-12T19:30:41+03:00
---
Everyone uses the JavaScript console for logging or debugging every once in a while. But there is a lot more to the [console](https://developer.mozilla.org/en-US/docs/Web/API/Console) object than `console.log()`.
### Computed property names
ES6 computed property names are particularly useful, as they can help you identify logged variables by adding a pair of curly braces around them.
```js
const x = 1, y = 2, z = 3;
console.log({x, y, z}); // {x: 1, y: 2, z: 3}
```
### console.trace()
[`console.trace()`](https://developer.mozilla.org/en-US/docs/Web/API/Console/trace) works the exact same as `console.log()`, but it also outputs the entire stack trace so you know exactly what's going on.
```js
const outer = () => {
const inner = () => console.trace('Hello');
inner();
};
outer();
/*
Hello
inner @ VM207:3
outer @ VM207:5
(anonymous) @ VM228:1
*/
```
### console.group()
[`console.group()`](https://developer.mozilla.org/en-US/docs/Web/API/Console/group) allows you to group logs into collapsable structures and is particularly useful when you have multiple logs.
```js
console.group('Outer'); // Create a group labelled 'Outer'
console.log('Hello'); // Log inside 'Outer'
console.groupCollapsed('Inner'); // Create a group labelled 'Inner', collapsed
console.log('Hellooooo'); // Log inside 'Inner'
console.groupEnd(); // End of current group, 'Inner'
console.groupEnd(); // End of current group, 'Outer'
console.log('Hi'); // Log outside of any groups
```
### Logging levels
There are a few more logging levels apart from `console.log()`, such as [`console.debug()`](https://developer.mozilla.org/en-US/docs/Web/API/Console/debug), [`console.info()`](https://developer.mozilla.org/en-US/docs/Web/API/Console/info), [`console.warn()`](https://developer.mozilla.org/en-US/docs/Web/API/Console/warn) and [`console.error()`](https://developer.mozilla.org/en-US/docs/Web/API/Console/error).
```js
console.debug('Debug message');
console.info('Useful information');
console.warn('This is a warning');
console.error('Something went wrong!');
```
### console.assert()
[`console.assert()`](https://developer.mozilla.org/en-US/docs/Web/API/console/assert) provides a handy way to only log something as an error when an assertion fails (i.e. when the first argument is `false`), otherwise skip the log entirely.
```js
const value = 10;
console.assert(value === 10, 'Value is not 10!'); // Nothing is logged
console.assert(value === 20, 'Value is not 20!'); // Logs "Value is not 20!"
```
### console.count()
You can use [`console.count()`](https://developer.mozilla.org/en-US/docs/Web/API/Console/count) to count how many times a piece of code has executed.
```js
Array.from({ length: 4 }).forEach(
() => console.count('items') // Call the counter labelled 'items'
);
/*
items: 1
items: 2
items: 3
items: 4
*/
console.countReset('items'); // Reset the counter labelled 'items'
```
### console.time()
[`console.time()`](https://developer.mozilla.org/en-US/docs/Web/API/Console/time) gives you a quick way to check the performance of your code, but should not be used for real benchmarking due to its low accuracy.
```js
console.time('slow comp'); // Start the 'slow comp' timer
console.timeLog('slow comp'); // Log the value of the 'slow comp' timer
console.timeEnd('slow comp'); // Stop and log the 'slow comp' timer
```
### CSS
Last but not least, you can use the `%c` string substitution expression in `console.log()` to apply CSS to parts of a log.
```js
console.log(
'CSS can make %cyour console logs%c %cawesome%c!', // String to format
// Each string is the CSS to apply for each consecutive %c
'color: #fff; background: #1e90ff; padding: 4px', // Apply styles
'', // Clear any styles
'color: #f00; font-weight: bold', // Apply styles
'' // Clear any styles
);
```

View File

@ -0,0 +1,57 @@
---
title: What is the difference between cookies, local storage, and session storage?
shortTitle: Cookies, local storage, and session storage
type: question
language: javascript
tags: [browser,webdev]
author: chalarangelo
cover: three-vases
excerpt: Learn the difference between cookies, local storage and session storage and start using the correct option for your needs.
dateModified: 2021-06-12T19:30:41+03:00
---
### Cookies
Cookies store small amounts of data that has to be sent back to the server with subsequent requests and their expiration can be set from either server or client. They are primarily used for server-side reading.
- Capacity: 4KB
- Accessible from: Any window
- Expiration: Manually set
- Storage location: Browser and server
- Sent with requests: Yes
- Blockable by users: Yes
- Editable by users: Yes
### Local storage
Local storage stores a larger amount of data on the client's computer in a key-value pair format and has no expiration date. Data is never transferred to the server and is accessible via JavaScript and HTML5.
- Capacity: 10MB
- Accessible from: Any window
- Expiration: Never
- Storage location: Browser only
- Sent with requests: No
- Blockable by users: Yes
- Editable by users: Yes
### Session storage
Session storage stores a larger amount of data on the client's computer only for the current session, expiring the data on tab close. Data is never transferred to the server and is accessible client-side from the same tab.
- Capacity: 5MB
- Accessible from: Same tab
- Expiration: On tab close
- Storage location: Browser only
- Sent with requests: No
- Blockable by users: Yes
- Editable by users: Yes
| | Cookies | Local storage | Session storage |
| -- | -- | -- | -- |
| Capacity | 4KB | 10MB | 5MB |
| Accessible from | Any window | Any window | Same tab |
| Expiration | Manually set | Never | On tab close |
| Storage location | Browser and server | Browser only | Browser only |
| Sent with requests | Yes | No | No |
| Blockable by users | Yes | Yes | Yes |
| Editable by users | Yes | Yes | Yes |

View File

@ -0,0 +1,55 @@
---
title: How can I copy text to clipboard with JavaScript?
shortTitle: Copy text to clipboard
type: question
language: javascript
tags: [browser]
author: chalarangelo
cover: typing
excerpt: Learn how to programmatically copy text to clipboard with a few lines of JavaScript and level up your web development skills.
dateModified: 2022-01-11T09:47:54+03:00
---
### Asynchronous Clipboard API
A very common need when building websites is the ability to copy text to clipboard with a single button click. If you only need to support modern browsers, it's highly recommended to use the asynchronous [Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API). It's supported in all modern browsers and provides an easy and secure way to update the clipboard's contents.
All you have to do is ensure `Navigator`, `Navigator.clipboard` and `Navigator.clipboard.writeText` are truthy and then call `Clipboard.writeText()` to copy the value to clipboard. In case anything goes wrong, you can use `Promise.reject()` to return a promise that rejects immediately and keep the return type consistent.
```js
const copyToClipboard = str => {
if (navigator && navigator.clipboard && navigator.clipboard.writeText)
return navigator.clipboard.writeText(str);
return Promise.reject('The Clipboard API is not available.');
};
```
This is pretty much how the [copyToClipboardAsync snippet](/js/s/copy-to-clipboard-async) is implemented and should work across all modern browsers.
### Document.execCommand('copy')
While support for the Clipboard API is pretty high across the board, you might need a fallback if you have to support older browsers. If that's the case, you can use `Document.execCommand('copy')` to do so. Here's a quick step-by-step guide:
1. Create a` <textarea>` element to be appended to the document. Set its value to the string you want to copy to the clipboard.
2. Append the `<textarea>` element to the current HTML document and use CSS to hide it to prevent flashing.
3. Use `HTMLInputElement.select()` to select the contents of the `<textarea>` element.
4. Use `Document.execCommand('copy')` to copy the contents of the `<textarea>` to the clipboard.
5. Remove the `<textarea>` element from the document.
```js
const copyToClipboard = str => {
const el = document.createElement('textarea');
el.value = str;
el.setAttribute('readonly', '');
el.style.position = 'absolute';
el.style.left = '-9999px';
document.body.appendChild(el);
el.select();
document.execCommand('copy');
document.body.removeChild(el);
};
```
Bear in mind that this method will not work everywhere, but only as a result of a user action (e.g. inside a `click` event listener), due to the way `Document.execCommand()` works.
There are a couple of other considerations, such as restoring the user's previous selection on the document, which can be easily handled with modern JavaScript. You can find the final code with these improvements implemented in the [copyToClipboard snippet](/js/s/copy-to-clipboard/).

View File

@ -0,0 +1,29 @@
---
title: What is CORS?
shortTitle: CORS explained
type: question
language: javascript
tags: [browser,webdev]
author: chalarangelo
cover: chill-surfing
excerpt: CORS (Cross-Origin Resource Sharing) trips up many developers, but it's pretty easy to wrap your head around.
dateModified: 2023-05-07T05:00:00-04:00
---
When it comes to HTTP, an **origin** is defined by several different aspects of a URL. As mentioned in a [previous article](/js/s/window-location-cheatsheet/), the origin is composed of the following:
- The **protocol** (e.g. `http` or `https`)
- The **hostname** (e.g. `30secondsofcode.org`)
- The **port** (e.g. `80` or `3000`)
As long as **all three** of these match, the browser considers the two URLs to be **same-origin**. If any of these aspects differ, the browser considers the two URLs to be **cross-origin**. It might be helpful to look at some examples of different origins to clarify:
- `http://30secondsofcode.org` and `https://www.30secondsofcode.org` (different protocols)
- `http://www.30secondsofcode.org` and `http://dev.30secondsofcode.org` (different hostnames)
- `https://30secondsofcode.org` and `https://30secondsofcode.org:3000` (different ports)
It's also important to note that the **path** (everything that comes after the hostname) is **not part of the origin**. This means that `https://30secondsofcode.org` and `https://30secondsofcode.org/articles` are considered to be the same origin.
When **CORS** (Cross-Origin Resource Sharing) is mentioned, it's usually in the context of **Same-Origin Policy**, a security feature implemented by web browsers. It **blocks web pages from making cross-origin requests** with the purpose of preventing malicious websites from making unauthorized requests to sensitive resources on other domains.
As this can be quite restrictive, CORS allows the server to specify which other domains are allowed to make requests to its resources. This is done through the use of **CORS headers**, `Origin` in the request and `Access-Control-Allow-Origin` in the response. This way, for example, API servers can allow requests from specific web pages, while still blocking requests from other domains.

View File

@ -0,0 +1,37 @@
---
title: How can I detect if Caps Lock is on with JavaScript?
shortTitle: Detect Caps Lock
type: question
language: javascript
tags: [browser,event]
author: chalarangelo
cover: keyboard
excerpt: If you need to check if Caps Lock is on when the user is typing in the browser, JavaScript's got you covered.
dateModified: 2021-06-12T19:30:41+03:00
---
Oftentimes, especially when creating password inputs, you need to check if the Caps Lock key is on and inform the user. You can do that using the [`KeyboardEvent.getModifierState()`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/getModifierState) method with a value of `'CapsLock'`. This means that you have to listen for a keyboard event on an element in order to check the state of the Caps Lock key:
```html
<form>
<label for="username">Username:</label>
<input id="username" name="username">
<label for="password">Password:</label>
<input id="password" name="password" type="password">
<span id="password-message" style="display: none">Caps Lock is on</span>
</form>
```
```js
const el = document.getElementById('password');
const msg = document.getElementById('password-message');
el.addEventListener('keyup', e => {
msg.style = e.getModifierState('CapsLock')
? 'display: block'
: 'display: none';
});
```
As you can see from the above example, the `'keyup'` event is used on our element of choice to then call `KeyboardEvent.getModifierState()` and determine the state of the `'CapsLock'` key. `'keydown'` and `'keypress'` might also work. However, after testing on multiple devices, it seems that using `'keyup'` is the preferred method as it works better across different OSes and browsers.

View File

@ -0,0 +1,50 @@
---
title: "Tip: Refactoring your for...in loops to avoid ESLint warnings"
shortTitle: Refactoring for...in loops to avoid ESLint warnings
type: tip
language: javascript
tags: [array,iterator,eslint]
author: chalarangelo
cover: typing
excerpt: ESLint is a really useful tool, but sometimes it gets in the way. Learn how to refactor code to get rid of a common warning.
dateModified: 2021-06-12T19:30:41+03:00
---
ESLint is one of my tools of choice, but oftentimes it gets in the way of work, due to the way it prefers me to do things. One of the warnings I have seen more times than I care to admit is the following:
> for..in loops iterate over the entire prototype chain, which is virtually never what you want. Use Object.{keys,values,entries}, and iterate over the resulting array.eslint(no-restricted-syntax)
And here are three refactoring options to deal with it:
### Object.keys()
`Object.keys()` has the exact same behavior as a `for...in` loop, so it can be used as a drop-in replacement:
```js
const data = [3, 4];
// Same as for (let k in data) console.log(k)
Object.keys(data).forEach(k => console.log(k));
// 0 1
```
### Object.values()
`Object.values()` is very similar to `Object.keys()`, but returns the values instead of the keys, which might be what you are really using the keys for:
```js
const data = [3, 4];
// Iterate over the values
Object.values(data).forEach(v => console.log(v));
// 3 4
```
### Object.entries()
Finally, if you need both key and value, `Object.entries()` has you covered:
```js
const data = [3, 4];
// Iterate over the data, returning key-value pairs
Object.entries(data).forEach(e => console.log(e[0], e[1]));
// [0, 3] [1, 4]
```

View File

@ -0,0 +1,22 @@
---
title: Mocking global object methods in Jest
type: story
language: javascript
tags: [testing]
author: chalarangelo
cover: trippy-chemicals
excerpt: Testing your code is important, but mocking can be tricky at times. Here's a quick guide on how to mock global object methods in Jest.
dateModified: 2022-03-27T05:00:00-04:00
---
Testing is a big part of the development process. It's also where a lot of mistakes can be overlooked, which can pile up and lead to hard-to-debug issues. A common problem is poorly-written mocks, especially regarding global objects and their methods. Let's take a look at how to mock global object methods in Jest.
When mocking global object methods in Jest, the optimal way to do so is using the `jest.spyOn()` method. It takes the object and name of the method you want to mock, and returns a mock function. The resulting mock function can then be chained to a mocked implementation or a mocked return value. For example:
```js
jest.spyOn(Math, 'random').mockReturnValue(0.123456789);
jest.spyOn(Date, 'now').mockReturnValue('123456789');
```
In this example, we mock two global object methods and return a fixed value. You could as easily mock their implementation using `mockFn.mockImplementation()`. Using either of these options allows you to get predictable values from the mocked methods. This comes in especially handy when working, for example, with `Math.random()` or `Date.now()`.

View File

@ -0,0 +1,21 @@
---
title: "Tip: Debugging Node.js using Chrome Developer Tools"
shortTitle: Debug Node.js with Chrome Developer Tools
type: tip
language: javascript
tags: [node,debugging]
author: chalarangelo
cover: bug
excerpt: Did you know you can use Chrome Developer Tools to debug your Node.js code? Find out how in this short guide.
dateModified: 2021-06-12T19:30:41+03:00
---
Node.js can be debugged using Chrome Developer Tools since `v6.3.0`. Here's a quick guide on how to do this:
1. Download and install Node.js `v6.3.0` or newer, if you don't already have it installed on your machine.
2. Run node with the `--inspect-brk` flag (e.g. `node --inspect-brk index.js`).
3. Open `about:inspect` in a new tab in Chrome. You should see something like the screenshot below.
4. Click `Open dedicated DevTools for Node` to open a new window connected to your Node.js instance.
5. Use the Developer Tools to debug your Node.js application!
![about:inspect page](./illustrations/chrome-debug-node.png)

View File

@ -0,0 +1,195 @@
---
title: Create a static file server with Node.js
shortTitle: Node.js static file server
type: story
language: javascript
tags: [node,server]
author: chalarangelo
cover: man-cup-laptop
excerpt: Create your own static file server with Node.js in just 70 lines of code.
dateModified: 2022-06-05T05:00:00-04:00
---
### A simple static file server
One of the simplest beginner backend projects you can create is a static file server. In its simplest form, a static file server will listen for requests and try to match the requested URL to a file on the local filesystem. Here's a minimal example of that in action:
```js
const fs = require('fs');
const http = require('http');
http.createServer((req, res) => {
fs.readFile(__dirname + req.url, (err, data) => {
if (err) {
res.writeHead(404, { 'Content-Type': 'text/html' });
res.end('404: File not found');
} else {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(data);
}
});
}).listen(8000);
```
In this code example, we're using the `fs` module to read the file at `__dirname + req.url`. If the file doesn't exist, we'll return a `404` error. Otherwise, we'll return the file. The `http` module is used to create the server that listens on port `8000`.
In theory, one could stop here and have a very basic static file server. However, there are a few considerations that could be taken into account. Let's explore them one by one, and see how we can address them.
### Modularity
First and foremost, we don't necessarily want to serve files from the same directory as our Node.js server. To address this problem, we would have to change the directory `fs.readFile()` looks for the file in. To accomplish this, we can specify a directory to serve files from and use the `path` module to resolve files from that directory. This way, we can also better handle different operating systems and environments.
Here's a short snippet on how to resolve a file path using the `path` module:
```js
const fs = require('fs');
const path = require('path');
const directoryName = './public';
const requestUrl = 'index.html';
const filePath = path.join(directoryName, requestUrl);
fs.readFile(filePath, (err, data) => {
// ...
});
```
### Security
Our next concern is security. Obviously, we don't want users prying around our machine unauthorized. Currently, it's not impossible to get access to files outside of the specified root directory (e.g. `GET /../../../`). To address this, we can use the `path` module again to check if the requested file is inside the root directory.
```js
const path = require('path');
const directoryName = './public';
const root = path.normalize(path.resolve(directoryName));
const requestUrl = 'index.html';
const filePath = path.join(root, fileName);
const isPathUnderRoot = path
.normalize(path.resolve(filePath))
.startsWith(root);
```
Similarly, we can ensure that users don't get access to sensitive files by checking the file type. For this to work, we can specify an array or object of supported file types and check the file's extension using the `path` module once again.
```js
const path = require('path');
const types = ['html', 'css', 'js', 'json'];
const requestUrl = 'index.html';
const extension = path.extname(requestUrl).slice(1);
const isTypeSupported = types.includes(extension);
```
### Omitting the HTML extension
A staple of most websites is the ability to omit the file extension from the URL when requesting an HTML page. It's a small quality of life improvement that users expect and it would be really nice to add to our static file server.
This is where things get a little tricky. To provide this functionality, we need to check for missing extensions and look up the appropriate HTML file. Bear in mind, though, that there are two possible matches for a URL such as `/my-page`. This path can either be matched by `/my-page.html` or `my-page/index.html`. To deal with this, we'll prioritize one over the other. In our case, we'll prioritize `/my-page.html` over `my-page/index.html`, but it's pretty easy to swap them the other way round.
To implement this, we can use the `fs` module to check if one of them exists and handle things appropriately. A special case would also need to be added for the root url (`/`) to match it to the `index.html` file.
```js
const fs = require('fs');
const path = require('path');
const directoryName = './public';
const root = path.normalize(path.resolve(directoryName));
const extension = path.extname(req.url).slice(1);
let fileName = requestUrl;
if (requestUrl === '/') fileName = 'index.html';
else if (!extension) {
try {
fs.accessSync(path.join(root, requestUrl + '.html'), fs.constants.F_OK);
fileName = requestUrl + '.html';
} catch (e) {
fileName = path.join(requestUrl, 'index.html');
}
}
```
### Final touches
After implementing all of the above, we can put everything together to create a static file server with all the functionality we need. I'll throw in a couple of finishing touches, such as logging requests to the console and handling a few more file types, and here's the final product:
```js
const fs = require('fs');
const http = require('http');
const path = require('path');
const port = 8000;
const directoryName = './public';
const types = {
html: 'text/html',
css: 'text/css',
js: 'application/javascript',
png: 'image/png',
jpg: 'image/jpeg',
jpeg: 'image/jpeg',
gif: 'image/gif',
json: 'application/json',
xml: 'application/xml',
};
const root = path.normalize(path.resolve(directoryName));
const server = http.createServer((req, res) => {
console.log(`${req.method} ${req.url}`);
const extension = path.extname(req.url).slice(1);
const type = extension ? types[extension] : types.html;
const supportedExtension = Boolean(type);
if (!supportedExtension) {
res.writeHead(404, { 'Content-Type': 'text/html' });
res.end('404: File not found');
return;
}
let fileName = req.url;
if (req.url === '/') fileName = 'index.html';
else if (!extension) {
try {
fs.accessSync(path.join(root, req.url + '.html'), fs.constants.F_OK);
fileName = req.url + '.html';
} catch (e) {
fileName = path.join(req.url, 'index.html');
}
}
const filePath = path.join(root, fileName);
const isPathUnderRoot = path
.normalize(path.resolve(filePath))
.startsWith(root);
if (!isPathUnderRoot) {
res.writeHead(404, { 'Content-Type': 'text/html' });
res.end('404: File not found');
return;
}
fs.readFile(filePath, (err, data) => {
if (err) {
res.writeHead(404, { 'Content-Type': 'text/html' });
res.end('404: File not found');
} else {
res.writeHead(200, { 'Content-Type': type });
res.end(data);
}
});
});
server.listen(port, () => {
console.log(`Server is listening on port ${port}`);
});
```
Not too bad, right? In just 70 lines of code, we managed to create a pretty decent static file server without using anything but core Node.js APIs.

View File

@ -0,0 +1,136 @@
---
title: Introduction to the Node.js test module
shortTitle: Node.js test module introduction
type: story
language: javascript
tags: [node,testing]
author: chalarangelo
cover: contemporary-desk
excerpt: The Node.js test module is a new testing tool that's still in its early stages. Learn more about it in this short introduction.
dateModified: 2023-04-30T05:00:00-04:00
---
A little while back, I stumbled upon the [Node.js test module](https://nodejs.org/docs/latest-v18.x/api/test.html). Having tried various JavaScript testing tools in recent years, I decided to set some time aside to test it and see how it works.
_Before you read any further, note that at the time of writing (April, 2023), this module is still **experimental** and is likely to change in future releases. While **not recommended for production use**, it's still worth learning about as it might come in handy later down the line._
So, how does the Node.js test module compare to the likes of Jest and Vitest? As expected, it's less feature-rich than other tools, which is understandable given that it's still in its early stages. However, the core functionality is there and it's very easy to use and set up as it **doesn't require third-party dependencies**.
Before you can use the module, you'll have to **import** it. You'll most likely have to import the **assertion module** as well. Here's how to import both:
```js
import test from 'node:test';
import assert from 'node:assert/strict';
```
Additionally, if you want to use methods such as `describe` or `it` from the test module, you'll have to import them as well. For example:
```js
import { describe, it } from 'node:test';
```
Now that you have the module imported, you can **start writing tests**. The test module provides a `test` function that takes two arguments in its simplest form:
- `name`: a string describing the test
- `fn`: a function containing the test logic
Let's look at an example:
```js
import test from 'node:test';
import assert from 'node:assert/strict';
test('my test', () => {
assert.strictEqual(1, 1);
});
```
This is pretty simple, but how does one **run the tests from the command line**? Contrary to most other tools, you don't need to create a new entry under your `package.json`'s `scripts` object. Instead, you can simply run the `node --test` command:
```shell
node --test
```
Running this command will match certain **file patterns**, such as files ending in `.test.js` and files under the `test` directory. You can read more about this in the [documentation](https://nodejs.org/docs/latest-v18.x/api/test.html#test-runner-execution-model). Additionally, you can manually specify one or more space-separated file patterns to run tests from:
```shell
node --test my-example.test.js
```
These basic instructions might cover some simple tests, but no testing tool is complete without more advanced features. Two important testing staples are **setup and teardown functions**. The test module provides `beforeEach` and `afterEach` functions that run before and after each test, respectively.
Note that you can import these functions and use them, but there's also another way. By making the test **asynchronous** and passing an argument to the test function, you can use the `beforeEach` and `afterEach` functions as methods of the test object. I've found this to be a bit more convenient, so I'll be using this method in the examples below. Here's what that looks like:
```js
import test from 'node:test';
import assert from 'node:assert/strict';
test('my test', async t => {
let a;
t.beforeEach(() => {
a = 1;
});
t.afterEach(() => {
a = 0;
});
await t.test('my subtest', () => {
assert.strictEqual(a, 1);
});
});
```
While all of this sounds pretty good, you might be wondering what a **real-world example** looks like. I cooked up a little function that mutates an array of numbers and returns a value just to demonstrate. The logic is not particularly complex or interesting, but it's enough to take the module for a spin. Let's have a look:
```js
import test, { describe } from 'node:test';
import assert from 'node:assert/strict';
const doubleAndSum = (arr, mod = 0) => {
let sum = 0;
Object.entries(arr).forEach(([i, v]) => {
if (v % 2 === mod) arr[i] = v * 2;
else sum += v;
});
return sum;
};
describe('doubleAndSum', () => {
let arr, sum;
test('when mod is 0', async t => {
t.beforeEach(() => {
arr = [1, 2, 3];
sum = doubleAndSum(arr, 0);
});
await t.test('sums the even values', () => {
assert.equal(sum, 4);
});
await t.test('doubles the even values', () => {
assert.equal(arr[1], 4);
});
});
test('when mod is 1', async t => {
t.beforeEach(() => {
arr = [1, 2, 3];
sum = doubleAndSum(arr, 1);
});
await t.test('sums the even values', () => {
assert.equal(sum, 2);
});
await t.test('doubles the odd values', () => {
assert.equal(arr[0], 2);
assert.equal(arr[2], 6);
});
});
});
```
And there you have it! Hopefully, by now you have a pretty good idea of what the Node.js test module is all about. There's a lot more to it, such as **coverage** reporting, **watch mode** and **mocking**, if you feel like digging deeper. You can find more information in the [documentation](https://nodejs.org/docs/latest-v18.x/api/test.html).

View File

@ -0,0 +1,22 @@
---
title: "Tip: Improve scroll listener performance"
shortTitle: Improve scroll listener performance
type: tip
language: javascript
tags: [browser,event]
author: chalarangelo
cover: chill-surfing
excerpt: Scroll listeners can easily become a performance bottleneck for your web application. Here's how to fix that.
dateModified: 2023-03-07T05:00:00-04:00
---
When working with scroll listeners in JavaScript, one can often run into performance issues. This is because scroll listeners are triggered on **every single scroll event**, which can be quite frequent. Most of the time, such listeners are used for infinite scrolling and lazy loading, meaning that the scroll event won't be intercepted. As such, `Event.preventDefault()` will not be called, guving us an optimization opportunity.
```js
window.addEventListener('scroll', () => {
// Do something
// Can't use `preventDefault` here
}, { passive: true });
```
As demonstrated in this code snippet, setting the `passive` option to `true` will enable certain **performance optimizations** in the browser. This way, the browser will know that it can safely skip the event queue and execute the scroll listener immediately. The result is a much smoother experience for the user, as the scroll event will be handled immediately, instead of being queued and handled later.

View File

@ -0,0 +1,62 @@
---
title: Regular Expressions Cheat Sheet
type: cheatsheet
language: javascript
tags: [string,regexp,cheatsheet]
author: chalarangelo
cover: tools
excerpt: Regular expressions are a very useful tool. Save this cheatsheet for when you need to look up their syntax and speed up your development.
dateModified: 2021-06-12T19:30:41+03:00
---
### Anchors
- `^`: start of the string or the start of a line in a multiline pattern
- `$`: end of the string or the end of a line in a multiline pattern
- `\b`: word boundary
- `\B`: not word boundary (opposite of `\b`)
Note: Anchors are non-quantifiable (i.e. cannot be followed by a quantifier).
### Character sequences
- `.`: any character except line breaks
- `\w`: any word character
- `\W`: any non-word character (opposite of `\w`)
- `\s`: any whitespace character
- `\S`: any non-whitespace character (opposite of `\s`)
- `\d`: any digit character
- `\D`: any non-digit character (opposite of `\d`)
- `[abc]`: a single character in the given set (here `a`, `b` or `c`)
- `[^abc]`: a single character not in the given set (opposite of `[abc]`)
- `[a-z]`: a single character in the given range (here between `a` and `z` inclusive)
- `[^a-z]`: a single character not in the given range (opposite of `[a-z]`)
- `[a-zA-Z]`: a single character in either of the given ranges
Note: Use `\` to escape special characters (e.g. `\`, `/`, `[`, `]`, `(`, `)`, `{`, `}` etc.).
### Quantifiers
- `a?`: zero or one of `a` (equal to `a{0,1}`)
- `a*`: zero or more of `a` (equal to `a{0,}`)
- `a+`: one or more of `a` (equal to `a{1,}`)
- `a{3}`: exactly 3 of `a`
- `a{3,}`: 3 or more of `a`
- `a{3,5}`: between 3 and 5 of `a` (inclusive)
Note: `a` is any valid quantifiable expression.
### Groups
- `(ab)`: match and capture everything enclosed (here exactly `ab`)
- `(a|b)`: match and capture either one character (here `a` or `b`)
- `(?:ab)`: match everything enclosed, without capturing
### Flags
- `g`: Global
- `m`: Multiline
- `i`: Case insensitive
- `u`: Unicode
Note that this cheatsheet is meant only as a starting point and is by no means a complete guide to all the features and nuances of regular expressions. You can also read [6 JavaScript Regular Expression features you can use today](/blog/s/6-javascript-regexp-tricks) for a deeper dive into some more advanced features.

View File

@ -0,0 +1,20 @@
---
title: "Tip: Select the focused DOM element"
shortTitle: Focused DOM element
type: tip
language: javascript
tags: [browser]
author: chalarangelo
cover: horse-sunset
excerpt: Here's a quick and easy way to select the currently focused DOM element in JavaScript.
dateModified: 2022-10-23T05:00:00-04:00
---
Finding the currently focused DOM element is trivial in modern CSS, using the `:focus` selector. You can also use it in JavaScript, in combination with `Document.querySelector()` to find the focused element. Yet, there's an even easier way to get the currently focused element in JavaScript, using the `Document.activeElement` property.
```js
const focusedElement = document.activeElement;
// `focusedElement` is the currently focused element
```
Note that focusable elements vary depending on browser and operating system. Additionally, you should remember that focus and selection (i.e. content highlighting) are not the same thing.

View File

@ -0,0 +1,49 @@
---
title: The case for trailing commas in JavaScript
shortTitle: Trailing commas in JavaScript
type: story
language: javascript
tags: [webdev]
author: chalarangelo
cover: contemporary-desk
excerpt: Trailing commas are not without controversy. Here's why I think you should use them.
dateModified: 2023-03-12T05:00:00-04:00
---
JavaScript's syntactic features are notorious for sparking debates among developers, with semicolons and tabs vs. spaces being the most common examples. However, trailing commas are a feature that isn't discussed as much, even though I believe there's merit to using them.
JavaScript has allowed trailing commas in array and object literals since the **ES5 specification**. Adding a trailing comma is completely valid and will not cause any errors or alter the code in any way. For example, the two statements below are exactly the same:
```js
[1, 2, 3]; // [1, 2, 3]
[1, 2, 3,]; // [1, 2, 3]
```
This example might look strange, and indeed it's not necessarily the prettiest sight. Where trailing commas start to feel more useful is when working with multiline arrays or objects. For example, consider the following object:
```js
const obj = {
a: 1,
b: 2,
c: 3
};
```
There's nothing wrong with this code snippet, except if we want to make a change to the object. In that case, we will have to add a comma to the last line, before adding a new property. A similar issue arises if we reorder the properties. In that case, a comma might end up missing from the line that was previously last. Finally, if we want consistency, removing the last property will also require the removal of the comma from the line before it.
None of these scenarios are uncommon, as you are well aware. Even more so, these start becoming a little more annoying when you factor in **version control**. A single property addition will require two lines to be altered. This can make diffs harder to read and review, and can also cause merge conflicts more often than you think.
I think it's clear by now that trailing commas can improve **code readability** and increase **diff clarity**. This is not a new idea and has been suggested by popular style guides for years, especially in multiline array and object literals.
As with most things, ESLint has a rule for this. The `comma-dangle` rule can be used to enforce trailing commas in both single-line and multiline situations. You can even go further and customize it for different types of literals, such as arrays, objects, functions, and imports/exports. My personal recommendation is to **enforce trailing commas only for multiline literals**:
```js
{
"rules": {
// Other rules ...
"comma-dangle": ["error", "always-multiline"]
}
}
```
This way you get all the benefits of trailing commas, without having to look at the potentially confusing trailing commas in single-line literals. As a final note, bear in mind that style decisions should always be discussed with your team and agreed upon.