Khái niệm cốt lõi
JavaScript dùng Call by Sharing (gọi bằng chia sẻ tham chiếu):
- Primitive (string, number, boolean, null, undefined, symbol, bigint): Truyền giá trị (copy value)
- Object (object, array, function): Truyền tham chiếu (copy reference)
Ví dụ minh họa
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pass Primitive vs Object</title>
<style>
body { font-family: 'Segoe UI', sans-serif; padding: 20px; }
.result { font-size: 16px; font-weight: 500; color: #6a1b9a; margin: 15px 0; padding: 15px; background: #f3e5f5; border-radius: 4px; white-space: pre-wrap; }
button { padding: 10px 20px; background: #1976d2; color: white; border: none; border-radius: 4px; cursor: pointer; }
</style>
</head>
<body>
<h1>Truyền Primitive vs Object vào hàm</h1>
<div class="result" id="result1"></div>
<div class="result" id="result2"></div>
<button id="btn">GỌI HÀM</button>
<h3>Nhấn nút để truyền primitive và object vào hàm</h3>
<script>
const btnEle = document.getElementById('btn');
const resEle1 = document.getElementById('result1');
const resEle2 = document.getElementById('result2');
const person = { name: 'Rohan', age: 12, city: 'Delhi' };
// Nhận object - có thể MUTATE nội dung
function printObj(obj) {
resEle1.textContent = `Name = ${obj.name}\nAge = ${obj.age}\nCity = ${obj.city}`;
// Mutate object gốc
obj.age = 999;
}
// Nhận primitive - KHÔNG thể thay đổi giá trị gốc
function printPrimitive(prim) {
resEle2.textContent = `Primitive value: ${prim}`;
prim = 999; // Chỉ thay đổi local parameter
}
btnEle.addEventListener('click', () => {
resEle1.textContent = '--- GỌI printObj(person) ---\n';
printObj(person);
resEle1.textContent += `\n\nSau khi gọi: person.age = ${person.age} (ĐÃ THAY ĐỔI!)`;
resEle2.textContent = '--- GỌI printPrimitive(22) ---\n';
let num = 22;
printPrimitive(num);
resEle2.textContent += `\n\nSau khi gọi: num = ${num} (KHÔNG THAY ĐỔI)`;
});
</script>
</body>
</html>
Kết quả
--- GỌI printObj(person) --- Name = Rohan Age = 12 City = Delhi Sau khi gọi: person.age = 999 (ĐÃ THAY ĐỔI!) --- GỌI printPrimitive(22) --- Primitive value: 22 Sau khi gọi: num = 22 (KHÔNG THAY ĐỔI)
Chi tiết: Object Reference
function mutate(obj) {
obj.name = 'Changed'; // ✅ Thay đổi object gốc
obj = { name: 'New' }; // ❌ Chỉ gán local parameter, không ảnh hưởng gốc
}
const original = { name: 'Original' };
mutate(original);
console.log(original.name); // 'Changed' - object gốc bị mutate
Cách tránh mutation không mong muốn
1. Spread/Rest (Shallow copy)
function safeFunc(obj) {
const copy = { ...obj }; // Shallow clone
copy.name = 'Safe';
return copy;
}
const original = { name: 'Original', nested: { x: 1 } };
const copy = safeFunc(original);
console.log(original.name); // 'Original' - an toàn
// ⚠️ Nhưng nested object vẫn shared!
copy.nested.x = 99;
console.log(original.nested.x); // 99 - nested bị thay đổi!
2. Deep Clone (JSON - limited)
const deepClone = obj => JSON.parse(JSON.stringify(obj));
// ❌ Không hỗ trợ: function, Date, RegExp, undefined, Symbol, circular ref
3. Structured Clone (Modern)
const deepClone = obj => structuredClone(obj);
// ✅ Hỗ trợ: Date, RegExp, Map, Set, ArrayBuffer, circular ref
// ❌ Không hỗ trợ: Function, DOM nodes, WeakMap/Set
4. Library: Lodash cloneDeep
import { cloneDeep } from 'lodash';
const safe = cloneDeep(original); // Hoàn toàn an toàn
Best Practice
- Coi object parameter là read-only trừ khi document rõ ràng là mutating
- Trả về object mới thay vì mutate (Immutable pattern)
- Dùng
constcho parameter (ngăn gán lại, nhưng không ngăn mutate) - TypeScript:
Readonly<T>cho compile-time protection