Built-in-like Range in JavaScript

Built-in-like Range in JavaScript

Scritto da Francesco Di Donato 6 ottobre 2021 2 minuti di lettura

Motivazione? Onestamente, nessuna. Zero. A parte il divertimento e lo studio.


Funzionalità di base

Inizi sovrascrivendo il prototype di Number con se stesso, ma prossato.

Object.setPrototypeOf(
	Number.prototype,
	new Proxy(Number.prototype, {
		// ...
	})
);

In questo modo, qualsiasi operazione normale relativa al prototype non viene persa.

Nel proxy ascolti l’accesso a qualsiasi proprietà tramite un getter. Il terzo argomento (receiver) è l‘“oggetto”, in questo caso il numero stesso - lo chiami start. È già del tipo giusto, number.

Il secondo argomento corrisponde al nome della proprietà, il suo typeof è infatti string.

Object.setPrototypeOf(
	Number.prototype,
	new Proxy(Number.prototype, {
		get(_, _end, start) {
			// _end -> '182' (typeof string)
			// start -> 42 (typeof number)
		}
	})
)(42)[182];

È sufficiente utilizzare parseInt e, se risulta ancora isNaN, lanciare un errore/avviso. Oppure, puoi semplicemente ignorarlo silenziosamente e tornare a start.

let end = parseInt(_end);
if (isNaN(end)) {
	// warning se errore
	// eventualmente, fallback
	return start;
}

Assicurato che il typeof end sia anch’esso number, puoi procedere a generare il range.

return Array(end - start + 1)
	.fill()
	.map((_, i) => start + i);

La funzionalità di base è completa. Ora il seguente codice è perfettamente valido.

(0)[5]; // [0, 1, 2, 3, 4, 5]

To make it not-end-inclusive, use Array(end - start) instead of Array(end - start + 1).


Range inverso

Per poter fare qualcosa di simile al seguente…

[5](0); // [5, 4, 3, 2, 1, 0]

Controlla se start > end e, in tal caso, scambia entrambi. Non dimenticare di ordinare il risultato in ordine decrescente.

Il codice è autoesplicativo.

Object.setPrototypeOf(
	Number.prototype,
	new Proxy(Number.prototype, {
		get(_, _end, start) {
			// where (start)[_end]

			let end = parseInt(_end);
			if (isNaN(end)) {
				// warning or error
				// eventually, fallback
				return start;
			}

			// sort behaviour - default ASC
			let s = +1;

			if (start > end) {
				// swap
				let tmp = start;
				start = end;
				end = tmp;

				// sort behaviour - DESC
				s = -1;
			}

			// generate range
			return Array(end - start + 1)
				.fill()
				.map((_, i) => start + i)
				.sort(() => s);
		}
	})
);

Result

42(
	// 42
	0
)[5](
	// [0, 1, 2, 3, 4, 5]
	0
)['foo'](
	// #fallback -> 0
	3
)[7](
	// [3, 4, 5, 6, 7]
	8
)[3]; // [8, 7, 6, 5, 4, 3]

Non avrei potuto fare la stessa cosa con una funzione range? Sì, probabilmente dovresti farlo con una funzione.

Lascia che questo sia un esercizio mentale e un modo per familiarizzare con il concetto di prototype e proxy.