Value vs Reference Semantics
JavaScript: ทุก object เป็น reference
หัวข้อที่มีชื่อว่า “JavaScript: ทุก object เป็น reference”ใน JavaScript และ TypeScript ค่าทุกอย่างที่ไม่ใช่ primitive (object, array, function) ถูกส่งเป็น reference การ assign object ให้ตัวแปรใหม่ไม่ได้ copy ข้อมูล — ตัวแปรทั้งสองชี้ไปที่ก้อนเดิม
// TypeScript — objects always shareconst a = { x: 1 };const b = a; // b points to the SAME objectb.x = 99;console.log(a.x); // 99 — a was also mutatedใน Go กฎมีความละเอียดกว่า: บาง type copy บาง type แชร์ การรู้ว่าอะไรเป็นอะไรป้องกัน bug ที่มองไม่เห็น
Struct และ Array เป็น value type
หัวข้อที่มีชื่อว่า “Struct และ Array เป็น value type”การ assign struct หรือ fixed-size array ให้ตัวแปรใหม่จะสร้าง copy เต็มรูปแบบ การแก้ไข copy ไม่กระทบต้นฉบับ
// TypeScript — objects always share (reference)interface Point { x: number; y: number; }const a: Point = { x: 1, y: 2 };const b = a; // b is the SAME objectb.x = 99;console.log(a.x); // 99 — mutated!
// Arrays also share:const arr1 = [1, 2, 3];const arr2 = arr1;arr2[0] = 99;console.log(arr1[0]); // 99// Go — structs copy on assigntype Point struct{ X, Y int }
a := Point{X: 1, Y: 2}b := a // b is a full COPYb.X = 99fmt.Println(a.X) // 1 — a is unchanged
// Fixed-size arrays also copyarr1 := [3]int{1, 2, 3}arr2 := arr1 // copyarr2[0] = 99fmt.Println(arr1[0]) // 1 — unchangedSlice และ Map เป็น reference type
หัวข้อที่มีชื่อว่า “Slice และ Map เป็น reference type”Slice และ map เก็บ pointer ภายในไปยังข้อมูลเบื้องล่าง การ assign ให้ตัวแปรใหม่จะ แชร์ pointer นั้น ทั้งสองตัวแปรจึงอ้างถึง array หรือ hash table เดิม
// TypeScript — arrays (like all objects) are referencesconst s1 = [1, 2, 3];const s2 = s1; // same arrays2[0] = 99;console.log(s1[0]); // 99
// Maps too:const m1 = new Map([["a", 1]]);const m2 = m1;m2.set("a", 99);console.log(m1.get("a")); // 99// Go — slices share the underlying arrays1 := []int{1, 2, 3}s2 := s1 // s2 shares s1's underlying arrays2[0] = 99fmt.Println(s1[0]) // 99 — shared!
// Maps always sharem1 := map[string]int{"a": 1}m2 := m1m2["a"] = 99fmt.Println(m1["a"]) // 99 — shared!
// To get a true copy, use copy() for slices:s3 := make([]int, len(s1))copy(s3, s1)s3[0] = 0fmt.Println(s1[0]) // 99 — s1 unchangedส่งให้ function
หัวข้อที่มีชื่อว่า “ส่งให้ function”กฎเดียวกันนี้ใช้ได้ตอนส่งค่าเข้า function โดย argument ที่เป็น struct จะมาเป็น copy ส่วน argument ที่เป็น slice/map จะแชร์ header
// TypeScript — objects always pass by referencefunction mutate(obj: { x: number }) { obj.x = 99; // mutates caller's object}const p = { x: 1 };mutate(p);console.log(p.x); // 99// Go — struct passes as copy; mutation doesn't affect callerfunc mutateStruct(p Point) { p.X = 99 // modifies the LOCAL copy only}
// Slice passes its header (pointer + len + cap) — shares datafunc mutateSlice(s []int) { s[0] = 99 // modifies the shared underlying array}
func main() { pt := Point{X: 1} mutateStruct(pt) fmt.Println(pt.X) // 1 — unchanged
sl := []int{1, 2, 3} mutateSlice(sl) fmt.Println(sl[0]) // 99 — shared data modified}package main
import "fmt"
type Point struct{ X, Y int }
func tryMutateStruct(p Point) { p.X = 999}
func tryMutateSlice(s []int) { s[0] = 999}
func main() { // Struct: value copy pt := Point{X: 1, Y: 2} tryMutateStruct(pt) fmt.Println("struct after func:", pt.X) // 1 — unchanged
// Slice: reference semantics sl := []int{1, 2, 3} tryMutateSlice(sl) fmt.Println("slice after func:", sl[0]) // 999 — mutated
// Array (fixed size): value copy arr := [3]int{10, 20, 30} arr2 := arr arr2[0] = 999 fmt.Println("array original:", arr[0]) // 10 — unchanged
// Explicit deep copy of slice original := []int{1, 2, 3} clone := make([]int, len(original)) copy(clone, original) clone[0] = 999 fmt.Println("original after clone mutation:", original[0]) // 1}Compiling on go.dev (Go 1.27)… offline? an in-browser runtime (~8 MB, Go 1.26) is used instead