Jenis Rekam & Tuple
Kekekalan secara bertahap menuju ke Javascript. Setelah penggunaan kekekalan API Temporal, jenis kekekalan lainnya, yaitu Record & Tuples, juga hadir di Javascript.
Perkenalan
Mari kita mulai dengan catatan. Jenis rekaman adalah struktur seperti objek yang sangat tidak dapat diubah. Untuk membuat rekaman, gunakan literal objek dan awali dengan simbol hash (#).
// 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 }
Primitif majemuk
Tipe Record & Tuples adalah gabungan primitif dengan nilai yang tidak dapat diubah . Cukup adil, tetapi apa sebenarnya arti definisi kikuk ini ?
Mari kita mulai dengan istilah primitif . Sejauh ini, Javascript hanya berisi tujuh tipe primitif :
- rangkaian
- nomor
- bigint
- boolean
- belum diartikan
- simbol
- batal
// 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} }
Rekam & Tuple bukanlah konsep baru. Sebagian besar bahasa fungsional memiliki tipe seperti ini. Plus, beberapa bahasa utama seperti C#, Python, dan Java juga baru-baru ini mengadopsinya. Bahkan dalam Javascript, telah ada pekerjaan sebelumnya yang dilakukan pada struktur data yang tidak dapat diubah yang disediakan oleh pustaka Immutable.JS dan lainnya.
Mari kita bandingkan catatan immutable.JS vs. sintaks catatan asli baru yang akan datang:
//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 !!!
Tolong, jangan salah paham, saya tidak mengkritik cara kerja Immutable.JS. Sama sekali tidak. Immutable.JS telah berhasil mempopulerkan konsep tipe yang tidak dapat diubah dalam Javascript. Tetap saja, saya pikir inilah saatnya untuk menantikan memiliki tipe asli yang tidak dapat diubah di toolbelt kita .
Fitur utama Rekam & Tuple
Sekarang mari kita lihat fitur utama dari tipe baru ini.
Persamaan
Kesetaraan selalu menjadi masalah yang rumit. Menggunakan objek klasik, Anda harus hanya mengandalkan persamaan referensial:
// 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
Situasi mengenai tupel sedikit berbeda. Kode ini berfungsi seperti yang diharapkan:
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
Kekekalan
Struktur yang tidak dapat diubah memiliki banyak manfaat dibandingkan dengan struktur yang dapat berubah. Dalam konteks Javascript, struktur yang tidak dapat diubah membantu kami mencegah modifikasi yang tidak disengaja dan membuat basis kode kami lebih mudah untuk dipikirkan.
Contoh kekekalan:
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}
Bekerja dengan Rekam & Tuple
Sejauh ini, saya telah menunjukkan kepada Anda sintaks untuk membuat tuple atau record literal. Namun, sintaks baru mendukung kasus penggunaan yang lebih canggih.
Buat instance yang dimodifikasi menggunakan spread
Anda dapat memodifikasi instans Anda menggunakan operator sebaran “…”. Ini mirip dengan metode with untuk objek Temporal.
Contoh operator spread:
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']
Mengubah array menjadi tupel dan objek menjadi rekaman
Sangat mudah untuk mengonversi instance antara tipe yang tidak dapat diubah dan pasangannya:
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 ]
Haruskah kita mengadopsi Rekam & Tuple?
Saya akan mengatakan ya, tanpa ragu-ragu. Saya telah menyimpulkan argumen saya di bawah ini.
Keakraban
Saya suka ide yang disebutkan dalam buku Kill with it Fire oleh Marriane Bellotti:
Adopsi teknologi baru didorong oleh keakraban atas keunggulan.
Konsep Marriane sangat cocok di sini. Penulis proposal Tuple & Rekam mencoba meniru semantik objek dan larik untuk membantu kami mengadopsi tipe primitif baru ini. Akibatnya, rekaman berperilaku serupa dengan objek atau larik. Jadi, adopsi harus cepat.
Pemodelan domain bisnis kami
Kekekalan dan perbandingan bersinar saat memodelkan domain. Pengembang aplikasi bisnis biasanya menggunakan banyak kelas domain yang mewakili Pengguna, Faktur, Laporan, dan objek dunia nyata lainnya. Dalam kasus ini, catatan mungkin bermanfaat.
Tetapi sebelum para fanatik OOP menggunakan garpu rumput mereka, izinkan saya mengklarifikasi: Saya tahu bahwa menggunakan catatan untuk kelas domain harus mengarah ke model domain anemia - anti-pola OOP. Namun demikian, juga memungkinkan untuk mendefinisikan perilaku untuk konsep domain dalam bahasa pemrograman menggunakan fungsi tingkat tinggi atau teknik fungsional lainnya. Jangan ragu untuk membaca lebih lanjut tentang ide ini di “ Apakah model domain fungsional selalu anemia ” atau “ Model Domain Anemia bukanlah Anti-Pola, ini adalah desain SOLID. ”
Pada akhirnya, saya mencoba model domain klasik dan anemia, dan yang pertama lebih cocok untuk saya, bahkan dalam bahasa OOP yang ketat.
Bagaimana dengan Tuple?
Sedangkan catatan cocok untuk memodelkan kelas domain Anda, tupel memiliki penggunaan yang lebih khusus. Tuple menyediakan cara mudah untuk mengembalikan atau meneruskan beberapa nilai dari atau ke suatu fungsi. Pengembang Javascript, terutama pengembang React, telah banyak menggunakan tupel setelah penugasan dekonstruksi mendarat di Javascript.
Kait useState React mengilustrasikannya dengan sempurna:
// [count, setCount] is a classic tuple-like array
const [count, setCount] = useState(0)
Kasus penggunaan lain untuk tupel adalah pola memoisasi . Sekarang, tanpa tupel asli, Anda perlu menggunakan trik untuk membuat serial argumen fungsi, seperti 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
}
}
Saya juga harus memperingatkan Anda, harap gunakan tupel dengan hati-hati. Terkadang use case Anda mungkin terlihat sangat baik untuk menggunakan tuple. Namun pada akhirnya, itu mungkin membawa lebih banyak kebingungan. Misalnya, koordinat geografis:
const [lat, long] = getCoordinates()
const [long, lat] = getCoordinates()
// it works in both cases:
const {lat,long} = getCoordinates()
const {long,lat} = getCoordinates()
Optimasi
Struktur yang tidak dapat diubah tidak hanya memberikan kode yang lebih bersih, tetapi juga memiliki efek kinerja yang tersembunyi. Karena JS adalah bahasa yang dinamis, kompiler JS harus membuat banyak tebakan tentang tata letak memori objek. Fakta bahwa Anda dapat menambahkan bidang objek baru kapan saja memperumit banyak hal. Bahasa statis menawarkan pengoptimalan yang lebih baik karena mereka mengetahui bentuk objek sebelumnya. Untuk mempelajari lebih lanjut, baca artikel berikut tentang pengoptimalan fungsi di V8 . Dengan tipe yang tidak dapat diubah, pembuat kompiler Javascript mungkin dapat mengoptimalkannya, serupa dengan pengetikan statis.
Status
Untuk Januari 2023, Record & Tuple masih dalam tahap 2, yang berarti mereka tidak akan mendarat dalam waktu dekat di browser kami. Namun, dengan plugin polyfill dan Babel yang berfungsi ini , Anda dapat mulai menjelajahi kemungkinan kasus penggunaan. Atau Anda bisa bermain dengan jenis baru ini di taman bermain ini .
Kesimpulan
Rekam & Tuple akan menjadi hal besar lainnya di Javascript. Memang, ini kurang berguna pada pandangan pertama daripada API Temporal baru atau API mewah seperti WebXR . Tetap saja, ini membawa konsep populer lainnya dari bahasa fungsional, yang pasti akan membuat pengkodean kita lebih mudah.
Kami adalah ACTUM Digital dan karya ini ditulis oleh Marek , Senior Front End Developer dari Apollo Division.
Jika Anda mencari bantuan untuk proyek atau inisiatif Anda, hubungi kami.

![Apa itu Linked List? [Bagian 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































