Skip to main content

Javascript

Coding Style

Use Prettier as a formatter and use ESLint as an inspector(with eslint-config-prettier).

Date

Date format

Use - for UTC time whereas use / for local time.

console.log(new Date('2019-01-01'));
/**
* -> Tue Jan 01 2019 08:00:00 GMT+0800 (Taipei Standard Time)
* UTC time
*/

console.log(new Date('2019/01/01'));
/**
* -> Tue Jan 01 2019 00:00:00 GMT+0800 (Taipei Standard Time)
* local time
*/

🔗 reference: 3/14/2012 blog from danvk Comparing FF/IE/Chrome on Parsing Date Strings

monthIndex

monthIndex is beginning with 0 for January to 11 for December.

new Date(2019, 1, 1, 0, 0, 0, 0);
// -> Fri Feb 01 2019 00:00:00 GMT+0800 (Taipei Standard Time)
new Date(2019, 0, 1, 0, 0, 0, 0);
// -> Tue Jan 01 2019 00:00:00 GMT+0800 (Taipei Standard Time)
new Date('1989-02-13').getMonth();
// -> 1

🔗 reference: Date

Date Libraries

Equality

🔗 reference: JavaScript-Equality-Table

Float Precision

Solutions:

big.js: minimalist; easy-to-use; precision specified in decimal places; precision applied to division only.

bignumber.js: bases 2-64; configuration options; NaN; Infinity; precision specified in decimal places; precision applied to division only; base prefixes.

decimal.js: bases 2-64; configuration options; NaN; Infinity; non-integer powers, exp, ln, log; precision specified in significant digits; precision always applied; random numbers.

🔗 reference: big.js vs. bignumber.js vs. decimal.js? #45

More info: Float Precision.

Interesting Encodes

NodeList

NodeList is live. document.querySelectorAll() returns a static NodeList.

{
const parentDom = document.createElement('div');
const childClass = 'testChild';
const { childNodes } = parentDom;
const appendChild = () => {
const children = document.createElement('div');
children.classList.add(childClass);
parentDom.appendChild(children);
};
appendChild();
const querySelectorAll = parentDom.querySelectorAll(`.${childClass}`);
const getElementsByClass = parentDom.getElementsByClassName(childClass);
console.log(childNodes.length); // 1
console.log(getElementsByClass.length); // 1
console.log(querySelectorAll.length); // 1
appendChild();
console.log(childNodes.length); // 2
console.log(getElementsByClass.length); // 2
console.log(querySelectorAll.length); // 1
}

🔗 reference: Node​List

Random

Math.random() does not provide cryptographically secure random numbers. Do not use them for anything related to security. Use the Web Crypto API instead, and more precisely the window.crypto.getRandomValues() method.

API:

getRandom(count: number): number[]
getRandom({
count?: number,
min?: number,
max?: number,
}): number[]
/**
* count:
* browser: 1 ~ 2 ** 14
* node: 1 ~ Out Of Memory
* min, max: 0 ~ 2 ** 32 - 1
*/

Inline:

const getRandom = (() => {
const maxMax = 2 ** 32 - 1;
const getRandom = (options) => {
let { count = 1, min = 0, max = maxMax } =
typeof options === 'object' ? options : { count: options };
if (min < 0) throw new Error(`[getRandom] min is less than 0, min: ${min}`);
if (max > maxMax)
throw new Error(`[getRandom] max is greater than ${maxMax}, max: ${max}`);
if (min > max)
throw new Error(
`[getRandom] min is greater than max, min: ${min}, max: ${max}`
);
return Array.from(
crypto.getRandomValues(new Uint32Array(count)),
(v) => min + (v % (max - min + 1))
);
};
return getRandom;
})();

Module:

import { inspect } from 'util';

const maxMax = 2 ** 32 - 1;

const getRandom = (options) => {
let { count = 1, min = 0, max = maxMax } =
typeof options === 'object' ? options : { count: options };
if (min < 0)
throw new Error(`[getRandom] min is less than 0, min: ${inspect(min)}`);
if (max > maxMax)
throw new Error(
`[getRandom] max is greater than ${inspect(maxMax)}, max: ${inspect(max)}`
);
if (min > max)
throw new Error(
`[getRandom] min is greater than max, min: ${inspect(
min
)}, max: ${inspect(max)}`
);
return Array.from(
crypto.getRandomValues(new Uint32Array(count)),
(v) => min + (v % (max - min + 1))
);
};

export default getRandom;

Typescript:

import { inspect } from 'util';

const maxMax: number = 2 ** 32 - 1;
interface Options {
count?: number;
min?: number;
max?: number;
}

const getRandom = (options?: number | Options): number[] => {
let { count = 1, min = 0, max = maxMax } =
typeof options === 'object' ? options : { count: options };
if (min < 0)
throw new Error(`[getRandom] min is less than 0, min: ${inspect(min)}`);
if (max > maxMax)
throw new Error(
`[getRandom] max is greater than ${inspect(maxMax)}, max: ${inspect(max)}`
);
if (min > max)
throw new Error(
`[getRandom] min is greater than max, min: ${inspect(
min
)}, max: ${inspect(max)}`
);
return Array.from(
crypto.getRandomValues(new Uint32Array(count)),
(v) => min + (v % (max - min + 1))
);
};

export default getRandom;

Node:

const crypto = require('crypto');
const { inspect } = require('util');

const maxMax = 2 ** 32 - 1;
const byte = 32 / 8;
const getRandom = (options) => {
let { count = 1, min = 0, max = maxMax } =
typeof options === 'object' ? options : { count: options };
if (min < 0)
throw new Error(`[getRandom] min is less than 0, min: ${inspect(min)}`);
if (max > maxMax)
throw new Error(
`[getRandom] max is greater than ${inspect(maxMax)}, max: ${inspect(max)}`
);
if (min > max)
throw new Error(
`[getRandom] min is greater than max, min: ${inspect(
min
)}, max: ${inspect(max)}`
);
const random = crypto.randomBytes(count * byte);
return Array.from(
Array(count),
(v, i) => min + (random.readUInt32BE(i * byte) % (max - min + 1))
);
};

module.exports = getRandom;

Node / multiple / async / thread:

const crypto = require('crypto');
const { inspect } = require('util');

const maxMax = 2 ** 32 - 1;
const getRandom = (options) =>
new Promise((resolve) => {
let { count = 1, min = 0, max = maxMax } =
typeof options === 'object' ? options : { count: options };
if (min < 0)
throw new Error(`[getRandom] min is less than 0, min: ${inspect(min)}`);
if (max > maxMax)
throw new Error(
`[getRandom] max is greater than ${inspect(maxMax)}, max: ${inspect(
max
)}`
);
if (min > max)
throw new Error(
`[getRandom] min is greater than max, min: ${inspect(
min
)}, max: ${inspect(max)}`
);
const a = new Uint32Array(count);
crypto.randomFill(a, (err, buf) => {
if (err) throw err;
resolve(Array.from(buf, (v) => min + (v % (max - min + 1))));
});
});

module.exports = getRandom;

Reference:

RegExp

RegExp.prototype​.test()

As with exec() (or in combination with it), test() called multiple times on the same global regular expression instance will advance past the previous match.

var regex1 = RegExp('foo*');
var regex2 = RegExp('foo*', 'g');
var str1 = 'table football';

console.log(regex1.test(str1));
// expected output: true

console.log(regex1.test(str1));
// expected output: true

console.log(regex2.test(str1));
// expected output: true

console.log(regex2.test(str1));
// expected output: false

🔗 reference: RegExp.prototype​.test()

SVG

  • SVGR

    Transforms SVG into React Components.

Whitespace: encodeURIComponent vs URLSearchParams

  • encodeURIComponent / encodeURI encode whitespace into %20
  • URLSearchParams encode whitespace into +.
{
console.log(new URLSearchParams('q=foo bar').toString()); // q=foo+bar
console.log(encodeURI('q=foo bar')); // q=foo%20bar
}

Server should decode both back to whitespace.

Solution: