Record e tipi di tupla
L'immutabilità si sta gradualmente facendo strada in Javascript. Dopo l'abbraccio dell'immutabilità dell'API temporale, anche altri tipi immutabili, vale a dire record e tuple, stanno arrivando anche a Javascript.
introduzione
Partiamo dalla cronaca. Un tipo di record è una struttura simile a un oggetto profondamente immutabile. Per creare un record, utilizzare un oggetto letterale e prefissarlo con un cancelletto (#).
// a classic object
const user = { name: "S. Holmes", age: 27 }
const address = { street: "221B Baker Street", city: 'London'}
// a record
const user = #{ name: "S. Holmes", age: 27 }
const address = #{ street: "221B Baker Street", city: 'London'}
// array
const enemies = ["James Moriarty", "Irene Adler"]
const friends = ["G. Lestrade", "John Hamish Watson"]
// tuple
const enemies = #[ "James Moriarty", "Irene Adler"]
const friends = #["G. Lestrade", "John Hamish Watson"]
#[10, 25] can be viewed as #{0: 10, 1: 25 }
Primitive composte
I tipi Records & Tuples sono primitive composte con valori immutabili . Abbastanza giusto, ma cosa significa esattamente questa definizione goffa ?
Iniziamo con il termine primitivi . Finora, Javascript contiene solo sette tipi primitivi :
- corda
- numero
- bigint
- booleano
- non definito
- simbolo
- nullo
// primitive but not compound
const str = "Primitive value"
const number = 1
const bool = true
const n = null
// the compound value:
// consists of two primitive values
const object = { name: 'Primitive value', number: 33 }
// consists of two primitive values & one function
const object = { name: 'Primitive value', number: 33, fn: () => {} }
// record with a nested record
const record = #{ name: 'Primitive value', number: 33,
nestedRecord : #{ number: 50} }
// tuple with a nested tuple and record
const tuple = #[ 'Primitive value', 33, #{ number: 50}, #[1, true, null] ]
// FORBIDDEN usage:
// record with a nested object
// it throws an error as it’s not a primitive value anymore
const record = #{ name: 'Primitive value', number: 33,
nestedObject: { number: 50} }
Record & Tuple non è un concetto nuovo. La maggior parte dei linguaggi funzionali ha questo tipo di tipi. Inoltre, anche più linguaggi tradizionali come C#, Python e Java li hanno recentemente adottati. Anche in Javascript, è stato svolto un lavoro precedente su strutture di dati immutabili fornite dalla libreria Immutable.JS e altri.
Confrontiamo i record di immutable.JS con la sintassi del nuovo record nativo in arrivo:
//Immutable.JS
const record1 = Record({ a: 1, b: 2 })
const record2 = record1.set('b', 50)
const b = record2.get('b')
const recordA = Record({ a: 1, b: 2 })
const recordB = Record({ a: 1, b: 2 })
recordA.equals(recordB) // true
recordA == (recordB) // false !!!
// Native records
const recordNative = #{ a: 1, b: 2 }
const recordNative2 = #{ ...recordNative, b: 50 }
const b = recordNative2.b
const recordA = #{ a: 1, b: 2 }
const recordB = #{ a: 1, b: 2 }
recordA == (recordB) // true !!!
Per favore, non fraintendermi, non sto criticando il modo di lavorare di Immutable.JS. Affatto. Immutable.JS ha svolto un ottimo lavoro nel rendere popolare il concetto di tipi immutabili in Javascript. Tuttavia, penso che sia giunto il momento di guardare avanti per avere i tipi immutabili nativi nella nostra cintura degli strumenti .
Caratteristiche principali di Record & Tuple
Ora diamo un'occhiata alle caratteristiche principali dei nuovi tipi.
Uguaglianza
L'uguaglianza è sempre stata una questione spinosa. Usando oggetti classici, devi fare affidamento esclusivamente sull'uguaglianza referenziale:
// Identity equality
const user = { name: 'Dr. Watson'}
const userRef = user
userRef == user // => true
const user = { name: 'Dr. Watson'}
const user2 = { name: 'Dr. Watson'}
user2 == user // => false
// Value equality
const user = #{ name: 'Dr. Watson'}
const user2 = #{ name: 'Dr. Watson'}
user2 == user // => true
const user1 = #{ name: 'S. Holmes', age: 27 }
const user2 = #{ age: 27, name: 'S. Holmes'}
user1 == user2 // => true
La situazione relativa alle tuple è leggermente diversa. Questo codice funziona come previsto:
const friends1 = #['G. Lestrade', 'John Hamish Watson']
const friends2 = #['G. Lestrade', 'John Hamish Watson']
friends1 == friends2 // => true
const friends1 = #['G. Lestrade', 'John Hamish Watson']
const friends2 = #['John Hamish Watson', 'G. Lestrade']
friends1 == friends2 // => false
Immutabilità
Le strutture immutabili hanno molti vantaggi rispetto alle loro controparti mutevoli. Nel contesto Javascript, le strutture immutabili ci aiutano a prevenire modifiche accidentali e rendono più facile ragionare sulla nostra base di codice.
Un esempio di immutabilità:
const primitive = 'string is an immutable primitive'
const primitive2 = primitive + '— change it'
primitive == primitive2 // => false
const obj1 = { id: 'A', value: 1}
const obj2 = obj1
// the following code changes the value of obj2.value
// but it also change obj1.value
obj2.value = 2
obj1 === obj2 // => still true
obj1.value === obj2.value // => true
// But with immutability, you can't change the record like a normal object.
const record1 = #{ id: 'A', value: 1}
record1.value = 2 // throws error
// You need to be more explicit to get changed value
const record2 = #{ ... record1, value: 2}
Lavorare con record e tupla
Finora vi ho mostrato la sintassi per la creazione di tuple o record letterali. Tuttavia, la nuova sintassi supporta casi d'uso più avanzati.
Crea un'istanza modificata utilizzando spread
Puoi modificare le tue istanze utilizzando un operatore di diffusione familiare "...". È simile al metodo with per gli oggetti temporali.
Esempio di operatore di diffusione:
const user = #{ name: 'S. Holmes', age: 27 }
// with immutable types, you are restricted to changing value directly:
user.age = 28 // throws error
// With spread operator, you can copy a record
// and modify it at the same time:
const userOneYearOlder = #{ ...user, age: 28}
// => #{ name: 'S. Holmes', get: 28 }
// Of course, it also means:
user == userOneYearOlder // => false
// Tuple
const friends1 = #['G. Lestrade', 'Dr. Watson']
const friends2 = #[...friends1, 'Mrs Hudson']
// => #['G. Lestrade', 'Dr. Watson', 'Mrs Hudson']
// You can modify an item at a particular position with the method 'with'
const friends3 = friends1.with(0, 'Mrs Hudson')
// => #['Mrs Hudson', 'Dr. Watson']
const friends1 = #['Dr. Watson', 'G. Lestrade']
const friends2 = friends1.toReversed()
// => ['G. Lestrade', 'Dr. Watson']
const friends3 = #['G. Lestrade', 'Dr. Watson']
const friends4 = friends3.toSorted()
// friends4 == #['Dr. Watson', 'G. Lestrade']
Conversione di array in tuple e oggetti in record
È facile convertire istanze tra tipi immutabili e le loro controparti:
const record = #{ name: 'S. Holmes', age: 27 }
// Note that any iterable of entries will work
const record2 = Record.fromEntries([['age', 27], #['name', 'S. Holmes']])
const tuple = Tuple(...[1, 2, 3])
// Note that an iterable will work as well
const tuple2 = Tuple.from([1, 2, 3])
record == #{ name: 'S. Holmes', age: 27 } // => true
tuple == #[1, 2, 3] // => true
// JS Compiler throws an error when using a non-primitive value
// TypeError: cannot use an object as a value in a record
Record({ a: {} })
// TypeError: cannot use an object as a value in a record
Tuple.from([{}, {} , {}])
const obj = { ...record }
const arr = [ ...tuple ]
Dovremmo adottare Record & Tuple?
Direi di sì, senza esitazione. Ho riassunto i miei argomenti di seguito.
Familiarità
Mi piace l'idea citata nel libro Kill with it Fire di Marriane Bellotti:
L'adozione di nuove tecnologie è guidata dalla familiarità piuttosto che dalla superiorità.
Il concetto di Marriane si adatta bene qui. Gli autori della proposta Tuple & Record hanno cercato di imitare la semantica dell'oggetto e dell'array per aiutarci ad adottare questi nuovi tipi primitivi. Di conseguenza, il record si comporta in modo simile agli oggetti o agli array. Pertanto, l'adozione dovrebbe essere rapida.
Modellare il nostro dominio aziendale
L'immutabilità e il confronto brillano durante la modellazione del dominio. Gli sviluppatori di applicazioni aziendali di solito utilizzano numerose classi di dominio che rappresentano utenti, fatture, report e altri oggetti del mondo reale. In questi casi, i record potrebbero essere utili.
Ma prima che i fanatici dell'OOP prendano in mano i loro forconi, lasciatemi chiarire: so che l'uso dei record per le classi di dominio dovrebbe portare a un modello di dominio anemico : anti-pattern OOP. Tuttavia, è anche possibile definire i comportamenti per i concetti di dominio in un linguaggio di programmazione utilizzando funzioni di ordine superiore o altre tecniche funzionali. Sentiti libero di leggere di più su questa idea in " I modelli di dominio funzionale sono sempre anemici " o " Il modello di dominio anemico non è un anti-pattern, è un progetto SOLIDO. "
Alla fine, ho provato sia il modello di dominio classico che quello anemico, e il primo mi si adattava meglio, anche in un linguaggio OOP rigoroso.
E le tuple?
Mentre i record sono adatti per modellare le classi di dominio, le tuple hanno un utilizzo più specializzato. Le tuple forniscono un modo semplice per restituire o passare più valori da o a una funzione. Gli sviluppatori Javascript, in particolare gli sviluppatori React, hanno utilizzato molto le tuple dopo che l' incarico di decostruzione è arrivato in Javascript.
L'hook useState di React lo ha illustrato perfettamente:
// [count, setCount] is a classic tuple-like array
const [count, setCount] = useState(0)
Un altro caso d'uso per le tuple è il modello di memoizzazione . Ora, senza tuple native, è necessario utilizzare trucchi per serializzare gli argomenti della funzione, come JSON.stringify :
const memoized = new Map()
function compute(...args) {
// a hack
const key = JSON.stringify({...args})
if (memoized.has(key))
{
return memoized.get(key)
}
else {
const value = peformSlowComputation(...args)
memoized[key] = value
return value
}
}
const memoized = new Map()
function compute(... args) {
// a nice and clean key
const key = #[...args]
if (memoized.has(key ))
{
return memoized.get(key)
}
else {
const value = performSlowComputation(...args)
memoized[key] = value
return value
}
}
Devo anche avvertirti, per favore usa le tuple con attenzione. A volte il tuo caso d'uso potrebbe sembrare perfetto per l'utilizzo di una tupla. Alla fine, però, potrebbe portare più confusione. Ad esempio, coordinate geografiche:
const [lat, long] = getCoordinates()
const [long, lat] = getCoordinates()
// it works in both cases:
const {lat,long} = getCoordinates()
const {long,lat} = getCoordinates()
Ottimizzazione
Le strutture immutabili non solo forniscono un codice più pulito, ma hanno anche effetti prestazionali nascosti. Poiché JS è un linguaggio dinamico, il compilatore JS deve fare molte ipotesi sul layout della memoria dell'oggetto. Il fatto che tu possa aggiungere il campo di un nuovo oggetto in qualsiasi momento complica le cose. I linguaggi statici offrono una migliore ottimizzazione perché conoscono in anticipo la forma degli oggetti. Per saperne di più, leggi il seguente articolo sull'ottimizzazione delle funzioni in V8 . Con tipi immutabili, gli autori di compilatori Javascript possono essere in grado di ottimizzarli, in modo simile alla tipizzazione statica.
Stato
Per quanto riguarda gennaio 2023, Record e Tuple sono ancora nella fase 2, il che significa che non arriveranno presto nei nostri browser. Tuttavia, con questo plug-in Polyfill e Babel funzionante , puoi iniziare a esplorare possibili casi d'uso. Oppure puoi giocare con questi nuovi tipi in questo parco giochi .
Conclusione
Record & Tuple sarà un'altra grande novità in Javascript. Certo, a prima vista è meno utile della nuova API temporale o di un'API fantasiosa come WebXR . Tuttavia, porta un altro concetto popolare dai linguaggi funzionali, che renderà sicuramente più semplice la nostra codifica.
Siamo ACTUM Digital e questo pezzo è stato scritto da Marek , Senior Front End Developer della Apollo Division.
Se cerchi aiuto per il tuo progetto o iniziativa, scrivici.

![Che cos'è un elenco collegato, comunque? [Parte 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































