ตัวลดที่ดีขึ้นด้วย Immer

เผยแพร่แล้ว: 2022-03-10
สรุปอย่างย่อ ↬ ในบทความนี้ เราจะมาเรียนรู้วิธีใช้ Immer ในการเขียนตัวลดขนาด เมื่อทำงานกับ React เราจะรักษาสถานะไว้มากมาย ในการอัปเดตสถานะของเรา เราต้องเขียนตัวลดจำนวนมาก การเขียนลดขนาดด้วยตนเองส่งผลให้โค้ดป่องซึ่งเราต้องสัมผัสเกือบทุกส่วนของรัฐ นี่เป็นเรื่องน่าเบื่อและเกิดข้อผิดพลาดได้ง่าย ในบทความนี้ เราจะมาดูกันว่า Immer นำความเรียบง่ายมาสู่กระบวนการเขียนตัวลดสถานะได้อย่างไร

ในฐานะนักพัฒนา React คุณควรคุ้นเคยกับหลักการที่ว่า รัฐไม่ควรกลายพันธุ์โดยตรงอยู่แล้ว คุณอาจสงสัยว่านั่นหมายถึงอะไร (พวกเราส่วนใหญ่มีความสับสนนั้นเมื่อเราเริ่มต้น)

บทช่วยสอนนี้จะใช้ความยุติธรรมกับสิ่งนั้น: คุณจะเข้าใจว่าสถานะที่ไม่เปลี่ยนรูปคืออะไรและจำเป็นสำหรับมัน คุณยังจะได้เรียนรู้วิธีการใช้ Immer เพื่อทำงานกับสถานะที่ไม่เปลี่ยนรูปและประโยชน์ของการใช้มัน คุณสามารถค้นหารหัสในบทความนี้ใน repo Github นี้

ความไม่เปลี่ยนรูปใน JavaScript และเหตุใดจึงสำคัญ

Immer.js เป็นไลบรารี JavaScript ขนาดเล็กที่เขียนขึ้นโดย Michel Weststrate ซึ่งภารกิจดังกล่าวคืออนุญาตให้คุณ "ทำงานกับสถานะที่ไม่เปลี่ยนรูปได้ในวิธีที่สะดวกกว่า"

แต่ก่อนที่จะดำดิ่งสู่ Immer เรามาทบทวนอย่างรวดเร็วเกี่ยวกับความไม่เปลี่ยนรูปใน JavaScript และเหตุใดจึงสำคัญในแอปพลิเคชัน React

มาตรฐาน ECMAScript (aka JavaScript) ล่าสุดกำหนดประเภทข้อมูลในตัวเก้าประเภท จากเก้าประเภทเหล่านี้ มีหกประเภทที่เรียกว่าค่า/ประเภท primitive พื้นฐานทั้งหกนี้ไม่มีการ undefined number string boolean bigint และ symbol การตรวจสอบอย่างง่ายด้วยตัวดำเนินการ typeof ของ JavaScript จะเปิดเผยประเภทของประเภทข้อมูลเหล่านี้

 console.log(typeof 5) // number console.log(typeof 'name') // string console.log(typeof (1 < 2)) // boolean console.log(typeof undefined) // undefined console.log(typeof Symbol('js')) // symbol console.log(typeof BigInt(900719925474)) // bigint

primitive คือค่าที่ไม่ใช่อ็อบเจกต์และไม่มีเมธอด สิ่งสำคัญที่สุดสำหรับการอภิปรายในปัจจุบันของเราคือข้อเท็จจริงที่ว่าค่าดั้งเดิมไม่สามารถเปลี่ยนแปลงได้เมื่อสร้างขึ้นแล้ว ดังนั้น ดึกดำบรรพ์จึงกล่าวกันว่า immutable

อีกสามประเภทที่เหลือคือ null object และ function นอกจากนี้เรายังสามารถตรวจสอบประเภทได้โดยใช้ประเภทของ typeof ดำเนินการ

 console.log(typeof null) // object console.log(typeof [0, 1]) // object console.log(typeof {name: 'name'}) // object const f = () => ({}) console.log(typeof f) // function

ประเภทเหล่านี้ mutable ได้ ซึ่งหมายความว่าสามารถเปลี่ยนแปลงค่าได้ตลอดเวลาหลังจากที่สร้างขึ้น

คุณอาจสงสัยว่าทำไมฉันถึงมีอาร์เรย์ [0, 1] อยู่ที่นั่น ใน JavaScriptland อาร์เรย์เป็นเพียงวัตถุประเภทพิเศษ ในกรณีที่คุณยังสงสัยเกี่ยวกับ null และความแตกต่างจาก undefined อย่างไร undefined หมายความว่าเราไม่ได้ตั้งค่าตัวแปรในขณะที่ null เป็นกรณีพิเศษสำหรับอ็อบเจกต์ หากคุณรู้ว่าบางสิ่งควรเป็นวัตถุ แต่ไม่มีวัตถุอยู่ คุณเพียงแค่ส่งคืน null

เพื่อแสดงตัวอย่างง่ายๆ ให้ลองเรียกใช้โค้ดด้านล่างในคอนโซลเบราว์เซอร์ของคุณ

 console.log('aeiou'.match(/[x]/gi)) // null console.log('xyzabc'.match(/[x]/gi)) // [ 'x' ]

String.prototype.match ควรส่งคืนอาร์เรย์ ซึ่งเป็นประเภท object เมื่อไม่พบวัตถุดังกล่าว จะส่งคืน null การส่งคืนที่ undefined ก็ไม่สมเหตุสมผลเช่นกัน

แค่นั้นพอ กลับมาพูดถึงความไม่เปลี่ยนรูปกัน

เพิ่มเติมหลังกระโดด! อ่านต่อด้านล่าง↓

ตามเอกสาร MDN:

“ทุกประเภทยกเว้นวัตถุกำหนดค่าที่ไม่เปลี่ยนรูป (นั่นคือค่าที่ไม่สามารถเปลี่ยนแปลงได้)”

คำสั่งนี้มีฟังก์ชันเนื่องจากเป็นอ็อบเจ็กต์ JavaScript ชนิดพิเศษ ดูคำจำกัดความของฟังก์ชันที่นี่

มาดูกันว่าในทางปฏิบัติประเภทข้อมูลที่ไม่แน่นอนและไม่เปลี่ยนแปลงนั้นมีความหมายอย่างไร ลองเรียกใช้โค้ดด้านล่างในคอนโซลเบราว์เซอร์ของคุณ

 let a = 5; let b = a console.log(`a: ${a}; b: ${b}`) // a: 5; b: 5 b = 7 console.log(`a: ${a}; b: ${b}`) // a: 5; b: 7

ผลลัพธ์ของเราแสดงให้เห็นว่าแม้ว่า b จะ "ได้มาจาก a " การเปลี่ยนค่าของ b จะไม่ส่งผลต่อค่าของ a สิ่งนี้เกิดขึ้นจากข้อเท็จจริงที่ว่าเมื่อกลไกจัดการ JavaScript รันคำสั่ง b = a จะสร้างตำแหน่งหน่วยความจำใหม่ที่แยกจากกัน ใส่ 5 ไว้ที่นั่น และจุด b ที่ตำแหน่งนั้น

แล้ววัตถุล่ะ? พิจารณารหัสด้านล่าง

 let c = { name: 'some name'} let d = c; console.log(`c: ${JSON.stringify(c)}; d: ${JSON.stringify(d)}`) // {"name":"some name"}; d: {"name":"some name"} d.name = 'new name' console.log(`c: ${JSON.stringify(c)}; d: ${JSON.stringify(d)}`) // {"name":"new name"}; d: {"name":"new name"}

เราจะเห็นว่าการเปลี่ยนคุณสมบัติชื่อผ่านตัวแปร d จะเปลี่ยนใน c ด้วย สิ่งนี้เกิดขึ้นจากข้อเท็จจริงที่ว่าเมื่อเอ็นจิ้น JavaScript รันคำสั่ง c = { name: 'some name ' } เอ็นจิ้น JavaScript จะสร้างช่องว่างในหน่วยความจำ วางอ็อบเจกต์ไว้ข้างใน และชี้ไปที่ c จากนั้น เมื่อรันคำสั่ง d = c น JavaScript จะชี้ d ไปยังตำแหน่งเดียวกัน มันไม่ได้สร้างตำแหน่งหน่วยความจำใหม่ ดังนั้นการเปลี่ยนแปลงใดๆ กับรายการใน d จึงเป็นการดำเนินการกับรายการใน c โดยปริยาย โดยไม่ต้องใช้ความพยายามมาก เราสามารถเห็นได้ว่าทำไมสิ่งนี้ถึงเป็นปัญหาในการสร้าง

ลองนึกภาพว่าคุณกำลังพัฒนาแอปพลิเคชัน React และบางแห่งที่คุณต้องการแสดงชื่อผู้ใช้เป็น some name โดยอ่านจากตัวแปร c แต่ที่อื่นคุณได้แนะนำจุดบกพร่องในโค้ดของคุณโดยจัดการอ็อบเจกต์ d ซึ่งจะส่งผลให้ชื่อผู้ใช้ปรากฏเป็น new name ถ้า c และ d เป็นพื้นฐาน เราจะไม่มีปัญหานั้น แต่พื้นฐานทั่วไปนั้นง่ายเกินไปสำหรับประเภทของสถานะที่แอปพลิเคชัน React ทั่วไปต้องรักษาไว้

นี่เป็นเหตุผลหลักว่าทำไมการรักษาสถานะที่ไม่เปลี่ยนรูปในแอปพลิเคชันของคุณจึงเป็นสิ่งสำคัญ ฉันแนะนำให้คุณตรวจสอบข้อควรพิจารณาอื่นๆ อีกสองสามข้อโดยอ่านส่วนสั้นๆ นี้จาก Immutable.js README: กรณีของการไม่เปลี่ยนรูป

เมื่อเข้าใจแล้วว่าทำไมเราถึงต้องการความไม่เปลี่ยนรูปในแอปพลิเคชัน React ตอนนี้เรามาดูกันว่า Immer จัดการกับปัญหาด้วยฟังก์ชันการ produce ได้อย่างไร

ฟังก์ชั่นการ produce ของ Immer

API หลักของ Immer นั้นเล็กมาก และฟังก์ชันหลักที่คุณจะใช้งานคือฟังก์ชันการ produce produce เพียงใช้สถานะเริ่มต้นและการโทรกลับที่กำหนดว่าสถานะควรจะกลายพันธุ์อย่างไร การเรียกกลับเองจะได้รับสำเนาของสถานะที่ทำการปรับปรุงทั้งหมด (ที่เหมือนกัน แต่ยังคงเป็นสำเนา) ในที่สุดก็ produce สถานะใหม่ที่ไม่เปลี่ยนรูปพร้อมกับการเปลี่ยนแปลงทั้งหมดที่ใช้

รูปแบบทั่วไปสำหรับการอัปเดตสถานะประเภทนี้คือ:

 // produce signature produce(state, callback) => nextState

เรามาดูกันว่ามันทำงานอย่างไรในทางปฏิบัติ

 import produce from 'immer' const initState = { pets: ['dog', 'cat'], packages: [ { name: 'react', installed: true }, { name: 'redux', installed: true }, ], } // to add a new package const newPackage = { name: 'immer', installed: false } const nextState = produce(initState, draft => { draft.packages.push(newPackage) })

ในโค้ดด้านบนนี้ เราเพียงแค่ส่งผ่านสถานะเริ่มต้นและการเรียกกลับที่ระบุว่าเราต้องการให้การกลายพันธุ์เกิดขึ้นอย่างไร มันง่ายอย่างนั้น เราไม่จำเป็นต้องสัมผัสส่วนอื่นของรัฐ มันปล่อย initState ไม่ถูกแตะต้อง และแบ่งปันโครงสร้างส่วนต่างๆ ของรัฐที่เราไม่ได้สัมผัสระหว่างสถานะเริ่มต้นและสถานะใหม่ ส่วนหนึ่งในรัฐของเราคืออาร์เรย์ pets การ produce d nextState เป็นแผนผังสถานะที่ไม่เปลี่ยนรูปซึ่งมีการเปลี่ยนแปลงที่เราได้ทำรวมถึงส่วนที่เราไม่ได้แก้ไข

ด้วยความรู้ที่เรียบง่าย แต่มีประโยชน์นี้ มาดูกันว่าการ produce สามารถช่วยให้เราลดความซับซ้อนของ React ของเราได้อย่างไร

การเขียนตัวลดขนาดด้วย Immer

สมมติว่าเรามีวัตถุสถานะที่กำหนดไว้ด้านล่าง

 const initState = { pets: ['dog', 'cat'], packages: [ { name: 'react', installed: true }, { name: 'redux', installed: true }, ], };

และเราต้องการเพิ่มวัตถุใหม่และในขั้นตอนต่อไป ให้ตั้งค่าคีย์ที่ installed true

 const newPackage = { name: 'immer', installed: false };

หากเราทำสิ่งนี้ตามปกติกับวัตถุ JavaScript และไวยากรณ์การแพร่กระจายอาร์เรย์ ตัวลดสถานะของเราอาจมีลักษณะดังนี้

 const updateReducer = (state = initState, action) => { switch (action.type) { case 'ADD_PACKAGE': return { ...state, packages: [...state.packages, action.package], }; case 'UPDATE_INSTALLED': return { ...state, packages: state.packages.map(pack => pack.name === action.name ? { ...pack, installed: action.installed } : pack ), }; default: return state; } };

เราจะเห็นได้ว่านี่เป็นรายละเอียดที่ไม่จำเป็นและมีแนวโน้มที่จะผิดพลาดสำหรับวัตถุสถานะที่ค่อนข้างง่ายนี้ เรายังต้องสัมผัสทุกส่วนของรัฐซึ่งไม่จำเป็น เรามาดูกันว่าเราจะทำให้สิ่งนี้ง่ายขึ้นด้วย Immer ได้อย่างไร

 const updateReducerWithProduce = (state = initState, action) => produce(state, draft => { switch (action.type) { case 'ADD_PACKAGE': draft.packages.push(action.package); break; case 'UPDATE_INSTALLED': { const package = draft.packages.filter(p => p.name === action.name)[0]; if (package) package.installed = action.installed; break; } default: break; } });
และด้วยโค้ดไม่กี่บรรทัด เราได้ลดความซับซ้อนของตัวลดของเราลงอย่างมาก นอกจากนี้ หากเราตกอยู่ในกรณีเริ่มต้น Immer จะคืนค่าสถานะแบบร่างโดยที่เราไม่ต้องดำเนินการใดๆ สังเกตว่ามีรหัสสำเร็จรูปน้อยลงและกำจัดการแพร่กระจายของรัฐ ด้วย Immer เรากังวลเฉพาะส่วนของรัฐที่เราต้องการอัปเดตเท่านั้น หากเราไม่พบรายการดังกล่าว เช่นเดียวกับในการดำเนินการ `UPDATE_INSTALLED' เราก็ดำเนินการต่อไปโดยไม่แตะต้องสิ่งอื่นใด ฟังก์ชัน 'ผลิต' ยังช่วยให้แกงกะหรี่อีกด้วย การส่งการเรียกกลับเป็นอาร์กิวเมนต์แรกที่ "ผลิต" มีวัตถุประสงค์เพื่อใช้สำหรับการแกง ลายเซ็นของแกงกะหรี่ 'ผลิต' คือ
 //curried produce signature produce(callback) => (state) => nextState
เรามาดูกันว่าเราจะปรับปรุงสถานะก่อนหน้าของเราด้วยผลิตภัณฑ์แกงกะหรี่ได้อย่างไร ผลิตภัณฑ์แกงกะหรี่ของเราจะมีลักษณะดังนี้:
 const curriedProduce = produce((draft, action) => { switch (action.type) { case 'ADD_PACKAGE': draft.packages.push(action.package); break; case 'SET_INSTALLED': { const package = draft.packages.filter(p => p.name === action.name)[0]; if (package) package.installed = action.installed; break; } default: break; } });

ฟังก์ชันผลิตผลแกงกะหรี่ยอมรับฟังก์ชันเป็นอาร์กิวเมนต์แรกและส่งกลับผลผลิตที่แกงกะหรี่ซึ่งตอนนี้ต้องการเพียงสถานะที่จะให้สถานะถัดไป อาร์กิวเมนต์แรกของฟังก์ชันคือสถานะแบบร่าง (ซึ่งจะได้มาจากสถานะที่จะถูกส่งต่อเมื่อเรียกผลิตภัณฑ์แกงกะหรี่นี้) จากนั้นติดตามอาร์กิวเมนต์ทุกจำนวนที่เราต้องการส่งต่อไปยังฟังก์ชัน

สิ่งที่เราต้องทำตอนนี้เพื่อใช้ฟังก์ชันนี้คือส่งผ่านในสถานะที่เราต้องการสร้างสถานะถัดไปและวัตถุการกระทำเช่นนั้น

 // add a new package to the starting state const nextState = curriedProduce(initState, { type: 'ADD_PACKAGE', package: newPackage, }); // update an item in the recently produced state const nextState2 = curriedProduce(nextState, { type: 'SET_INSTALLED', name: 'immer', installed: true, });

โปรดทราบว่าในแอปพลิเคชัน React เมื่อใช้ useReducer hook เราไม่จำเป็นต้องส่งสถานะอย่างชัดแจ้งเหมือนที่ฉันได้ทำไว้ข้างต้น เพราะเราจะดูแลเรื่องนั้นเอง

คุณอาจสงสัยว่า Immer จะได้รับ hook เหมือนทุกอย่างใน React หรือไม่? คุณอยู่ในบริษัทพร้อมกับข่าวดี Immer มีสอง hooks สำหรับการทำงานกับสถานะ: useImmer และ useImmerReducer hooks เรามาดูกันว่าพวกเขาทำงานอย่างไร

การใช้ the useImmer และ useImmerReducer Hooks

คำอธิบายที่ดีที่สุดของ useImmer hook มาจาก use-immer README เอง

useImmer(initialState) คล้ายกับ useState มาก ฟังก์ชันส่งคืน tuple ค่าแรกของ tuple คือสถานะปัจจุบัน ฟังก์ชันที่สองคือฟังก์ชัน updater ซึ่งยอมรับฟังก์ชัน immer Producer ซึ่ง draft สามารถกลายพันธุ์ได้อย่างอิสระ จนกว่าผู้ผลิตจะสิ้นสุดและจะทำการเปลี่ยนแปลง ไม่เปลี่ยนรูปและกลายเป็นสถานะต่อไป

หากต้องการใช้ hooks เหล่านี้ คุณต้องติดตั้งแยกต่างหาก นอกเหนือจากไลบรารี Immer หลัก

 yarn add immer use-immer

ในแง่ของ useImmer hook มีลักษณะดังนี้

 import React from "react"; import { useImmer } from "use-immer"; const initState = {} const [ data, updateData ] = useImmer(initState)

และมันก็ง่ายอย่างนั้น คุณสามารถพูดได้ว่ามันคือ useState ของ React แต่มีสเตียรอยด์เล็กน้อย การใช้ฟังก์ชั่นอัพเดทนั้นง่ายมาก ได้รับสถานะร่างและคุณสามารถแก้ไขได้มากเท่าที่คุณต้องการด้านล่าง

 // make changes to data updateData(draft => { // modify the draft as much as you want. })

ผู้สร้าง Immer ได้จัดเตรียมตัวอย่างโค้ดแซนด์บ็อกซ์ซึ่งคุณสามารถทดลองเล่นเพื่อดูว่ามันทำงานอย่างไร

useImmerReducer นั้นใช้งานง่ายเหมือนกันหากคุณเคยใช้เบ็ด useReducer ของ React มีลายเซ็นที่คล้ายกัน มาดูกันว่าหน้าตาจะเป็นอย่างไรในแง่ของโค้ด

 import React from "react"; import { useImmerReducer } from "use-immer"; const initState = {} const reducer = (draft, action) => { switch(action.type) { default: break; } } const [data, dataDispatch] = useImmerReducer(reducer, initState);

เราจะเห็นได้ว่าตัวลดขนาดได้รับสถานะ draft ซึ่งเราสามารถปรับเปลี่ยนได้มากเท่าที่เราต้องการ นอกจากนี้ยังมีตัวอย่างโค้ดแซนด์บ็อกซ์ให้คุณทดลองด้วย

และนั่นคือความง่ายในการใช้ Immer hooks แต่ในกรณีที่คุณยังคงสงสัยว่าเหตุใดคุณจึงควรใช้ Immer ในโครงการของคุณ นี่คือบทสรุปของเหตุผลที่สำคัญที่สุดบางประการที่ฉันพบว่าใช้ Immer

ทำไมคุณควรใช้ Immer

หากคุณได้เขียนตรรกะการจัดการสถานะไว้เป็นระยะเวลาหนึ่ง คุณจะประทับใจกับความเรียบง่ายที่เสนอโดย Immer ได้อย่างรวดเร็ว แต่นั่นไม่ใช่ข้อดีเพียงอย่างเดียวของ Immer

เมื่อคุณใช้ Immer คุณจะต้องเขียนโค้ดต้นแบบน้อยลง ตามที่เราเคยเห็นด้วยตัวลดขนาดที่ค่อนข้างง่าย นอกจากนี้ยังทำให้การอัปเดตเชิงลึกค่อนข้างง่าย

ด้วยไลบรารี่เช่น Immutable.js คุณต้องเรียนรู้ API ใหม่เพื่อเก็บเกี่ยวผลประโยชน์ของการไม่เปลี่ยนรูป แต่ด้วย Immer คุณจะบรรลุสิ่งเดียวกันด้วย JavaScript Objects , Arrays , Sets และ Maps ปกติ ไม่มีอะไรใหม่ให้เรียนรู้

Immer ยังจัดเตรียมการแบ่งปันโครงสร้างตามค่าเริ่มต้น นี่หมายความว่าเมื่อคุณทำการเปลี่ยนแปลงกับอ็อบเจ็กต์สถานะ Immer จะแบ่งปันส่วนที่ไม่เปลี่ยนแปลงของรัฐระหว่างสถานะใหม่กับสถานะก่อนหน้าโดยอัตโนมัติ

ด้วย Immer คุณยังได้รับการแช่แข็งวัตถุโดยอัตโนมัติ ซึ่งหมายความว่าคุณไม่สามารถเปลี่ยนแปลงสถานะที่ produced ได้ ตัวอย่างเช่น เมื่อฉันเริ่มใช้ Immer ฉันพยายามใช้วิธี sort กับอาร์เรย์ของวัตถุที่ส่งคืนโดยฟังก์ชันการผลิตของ Immer มันเกิดข้อผิดพลาดบอกฉันว่าฉันไม่สามารถเปลี่ยนแปลงอาร์เรย์ได้ ฉันต้องใช้วิธีอาร์เรย์สไลซ์ก่อนที่จะใช้ sort อีกครั้งที่ nextState ที่ผลิตขึ้นนั้นเป็นต้นไม้สถานะที่ไม่เปลี่ยนรูป

Immer ยังพิมพ์ได้ดีและมีขนาดเล็กมากเพียง 3KB เมื่อ gzipped

บทสรุป

เมื่อพูดถึงการจัดการการอัปเดตสถานะ การใช้ Immer ไม่ใช่เรื่องง่ายสำหรับฉัน เป็นไลบรารี่ขนาดเล็กมากที่ช่วยให้คุณใช้ทุกสิ่งที่คุณได้เรียนรู้เกี่ยวกับ JavaScript ต่อไปได้โดยไม่ต้องพยายามเรียนรู้สิ่งใหม่ทั้งหมด ฉันแนะนำให้คุณติดตั้งในโครงการของคุณและเริ่มใช้งานได้ทันที คุณสามารถเพิ่มการใช้งานในโครงการที่มีอยู่และอัปเดตตัวลดของคุณทีละน้อย

ฉันยังสนับสนุนให้คุณอ่านโพสต์บล็อกแนะนำ Immer โดย Michael Weststrate ส่วนที่ฉันพบว่าน่าสนใจเป็นพิเศษคือ "Immer ทำงานอย่างไร" ส่วนที่อธิบายวิธีที่ Immer ใช้ประโยชน์จากคุณลักษณะทางภาษา เช่น พร็อกซี่และแนวคิด เช่น การคัดลอกเมื่อเขียน

ฉันยังสนับสนุนให้คุณดูโพสต์บนบล็อกนี้: ความไม่เปลี่ยนรูปใน JavaScript: มุมมองที่ตรงกันข้าม ซึ่งผู้แต่ง Steven de Salas ได้นำเสนอความคิดของเขาเกี่ยวกับข้อดีของการใฝ่หาความไม่เปลี่ยนรูป

ฉันหวังว่าสิ่งที่คุณได้เรียนรู้ในโพสต์นี้จะช่วยให้คุณเริ่มใช้ Immer ได้ทันที

แหล่งข้อมูลที่เกี่ยวข้อง

  1. use-immer , GitHub
  2. อิมเมอร์, GitHub
  3. function , เอกสารเว็บ MDN, Mozilla
  4. proxy , เอกสารเว็บ MDN, Mozilla
  5. วัตถุ (วิทยาการคอมพิวเตอร์), Wikipedia
  6. “ไม่เปลี่ยนรูปใน JS” Orji Chidi Matthew, GitHub
  7. “ECMAScript ประเภทข้อมูลและค่า” Ecma International
  8. คอลเลกชันที่ไม่เปลี่ยนรูปสำหรับ JavaScript, Immutable.js , GitHub
  9. “กรณีของการไม่เปลี่ยนรูป” Immutable.js , GitHub