Rename language articles
This commit is contained in:
54
snippets/js/s/10-vs-code-extensions-for-js-developers.md
Normal file
54
snippets/js/s/10-vs-code-extensions-for-js-developers.md
Normal 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.
|
||||
59
snippets/js/s/4-array-methods.md
Normal file
59
snippets/js/s/4-array-methods.md
Normal 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]
|
||||
```
|
||||
|
||||

|
||||
|
||||
### 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
|
||||
```
|
||||
121
snippets/js/s/6-regexp-tricks.md
Normal file
121
snippets/js/s/6-regexp-tricks.md
Normal 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: 'γεια'
|
||||
]
|
||||
*/
|
||||
```
|
||||
113
snippets/js/s/async-cheatsheet.md
Normal file
113
snippets/js/s/async-cheatsheet.md
Normal 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
|
||||
}
|
||||
}
|
||||
```
|
||||
64
snippets/js/s/big-o-cheatsheet.md
Normal file
64
snippets/js/s/big-o-cheatsheet.md
Normal 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:
|
||||
|
||||

|
||||
|
||||
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) |
|
||||
77
snippets/js/s/code-anatomy-chaining-reduce-for-loop.md
Normal file
77
snippets/js/s/code-anatomy-chaining-reduce-for-loop.md
Normal 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).
|
||||
122
snippets/js/s/code-anatomy-optimizing-recursion.md
Normal file
122
snippets/js/s/code-anatomy-optimizing-recursion.md
Normal 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.
|
||||
133
snippets/js/s/common-regexp-cheatsheet.md
Normal file
133
snippets/js/s/common-regexp-cheatsheet.md
Normal 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
|
||||
```
|
||||
119
snippets/js/s/console-log-cheatsheet.md
Normal file
119
snippets/js/s/console-log-cheatsheet.md
Normal 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
|
||||
);
|
||||
```
|
||||
57
snippets/js/s/cookies-local-storage-session.md
Normal file
57
snippets/js/s/cookies-local-storage-session.md
Normal 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 |
|
||||
55
snippets/js/s/copy-text-to-clipboard.md
Normal file
55
snippets/js/s/copy-text-to-clipboard.md
Normal 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/).
|
||||
29
snippets/js/s/cors-explained.md
Normal file
29
snippets/js/s/cors-explained.md
Normal 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.
|
||||
37
snippets/js/s/detect-caps-lock-is-on.md
Normal file
37
snippets/js/s/detect-caps-lock-is-on.md
Normal 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.
|
||||
50
snippets/js/s/eslint-refactor-for-in.md
Normal file
50
snippets/js/s/eslint-refactor-for-in.md
Normal 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]
|
||||
```
|
||||
22
snippets/js/s/jest-mock-global-methods.md
Normal file
22
snippets/js/s/jest-mock-global-methods.md
Normal 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()`.
|
||||
21
snippets/js/s/nodejs-chrome-debugging.md
Normal file
21
snippets/js/s/nodejs-chrome-debugging.md
Normal 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!
|
||||
|
||||

|
||||
195
snippets/js/s/nodejs-static-file-server.md
Normal file
195
snippets/js/s/nodejs-static-file-server.md
Normal 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.
|
||||
136
snippets/js/s/nodejs-test-module-introduction.md
Normal file
136
snippets/js/s/nodejs-test-module-introduction.md
Normal 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).
|
||||
22
snippets/js/s/passive-scroll-listener-performance.md
Normal file
22
snippets/js/s/passive-scroll-listener-performance.md
Normal 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.
|
||||
62
snippets/js/s/regexp-cheatsheet.md
Normal file
62
snippets/js/s/regexp-cheatsheet.md
Normal 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.
|
||||
20
snippets/js/s/select-focused-dom-element.md
Normal file
20
snippets/js/s/select-focused-dom-element.md
Normal 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.
|
||||
49
snippets/js/s/the-case-for-trailing-commas.md
Normal file
49
snippets/js/s/the-case-for-trailing-commas.md
Normal 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.
|
||||
Reference in New Issue
Block a user