Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> JavaScript

Truyền kiểu nguyên thủy và đối tượng vào hàm trong JavaScript

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 const cho parameter (ngăn gán lại, nhưng không ngăn mutate)
  • TypeScript: Readonly<T> cho compile-time protection