Đôi điều về Object Reference trong JavaScript. Nhiều lúc quên thật phiền toái!

Đôi điều về Object Reference trong JavaScript. Nhiều lúc quên thật phiền toái!

Tin ngắn hàng ngày dành cho bạn
  • Mấy hôm trước OpenAI giới thiệu Deep Research - một công cụ duyệt web để nghiên cứu và cho ra một bản tổng hợp chỉ trong vài chục phút - so với nhiều giờ làm việc đối với con người, theo như họ công bố.

    Tính năng này hiện chỉ có sẵn cho người dùng Pro. Mặc dù chưa được dùng thử, nhưng qua nhiều bài viết đều nhấn mạnh vào sự ấn tượng trước khả năng của công cụ mới này. Nếu vẫn chưa biết Deep Research làm được gì thì bạn cứ hình dung như thế này: Nói với nó "Tôi cần thông tin nghiên cứu về lượng tiêu thụ cà phê của thế giới trong năm ngoái". Thế thôi! Ngồi chờ một lúc để nó tìm kiếm và tổng hợp lại kết quả và gửi lại cho bạn một bài báo cáo chi tiết. Chà, ghê thật chứ!

    Ngay lập tức huggingface đã lên một bài viết cố gắng tái tạo lại công cụ này theo cách của họ. Chi tiết tại Open-source DeepResearch – Freeing our search agents. Và không có gì ngạc nhiên khi cả 2 đều mang hơi hướng của AI Agents.

    » Xem thêm
  • Sống đủ lâu trong thế giới Internet, bạn có thể thấy rằng mọi người ở đây khá háo hức chạy theo xu hướng và chúng lan truyền với tốc độ chóng mặt.

    Chỉ vài tháng trước, chúng ta vẫn còn kinh ngạc về trí thông minh của các mô hình ngôn ngữ lớn (LLM) có thể trả lời giống như con người, và ngay sau đó, chúng đã được cập nhật với khả năng suy nghĩ và lý luận đáng kinh ngạc. Chúng được ứng dụng rộng rãi không chỉ trong lĩnh vực lập trình. Gần đây, thuật ngữ AI Agents đã tạo nên một sự khuấy động.

    Vậy, AI Agents là gì? Trong bài viết ngắn này, tất nhiên là không thể đưa ra một định nghĩa ngắn gọn nhưng toàn diện. Bạn đọc có thể tham khảo bài viết rất chi tiết này tại đây Agents | Chip Huyền. Để dễ hình dung hơn, AI Agents có thể được coi là một người hoặc một thực thể nào đó. Bản thân các Agents được trang bị tất cả các công cụ cần thiết. Từ đó, các Agents có thể kết hợp chúng để hoàn thành một nhiệm vụ mà chúng ta giao.

    Vẫn còn hơi mơ hồ phải không? Một ví dụ thực tế là khi bạn ra lệnh cho các Agents truy cập Facebook vào lúc 8 giờ tối mỗi ngày, kiểm tra bất kỳ tin tức nổi bật nào từ bạn bè, sau đó gửi tóm tắt đến Telegram. Vậy là xong!

    » Xem thêm
  • Hôm qua đến nay, lượt truy cập tới từ Facebook tăng đột biến. Thường như thế là do ai đó chia sẻ bài viết của blog vào một nhóm nào đó.

    Cơ mà lần này là liên kết trực tiếp đến trang chủ luôn. Tò mò ghê, không biết ai chia sẻ, chia sẻ ở đâu nữa. Muốn biết để tìm hiểu "insight" ghê 🥹

    » Xem thêm

Vấn đề

Object Reference là một khái niệm thể hiện một biến tham chiếu, tức là thay vì lưu trữ giá trị thực nó lưu lại địa chỉ ô nhớ & thao tác với dữ liệu dựa trên địa chỉ đó. Điều này giúp tiết kiệm bộ nhớ cho các ứng dụng. Tuy nhiên cũng không ít điều phiền toái từ nó mà ra.

Kiểu dữ liệu nguyên thuỷ

Trong Javascript, chúng ta có những kiểu dữ liệu "nguyên thuỷ" (Primitive Types) bao gồm: undefinednullstringnumberboolean và symbol.

Các biến khi được khởi tạo với các giá trị này sẽ được cấp pháp một ô nhớ nhất định không có liên quan đến nhau. Ví dụ:

let name = "estacks";
let name2 = name; // estacks
name = "estacks.icu";
console.log(name); // estacks.icu
console.log(name2); //estacks

Bất kì khi nào thay đổi biến name thì những biến trước được gán bằng name vẫn giữ nguyên giá trị. Điều này cũng tương tự với kiểu dữ liệu number, boolean...

Object & Array

Không giống với Primitive Types, Object & Array, khi có nhiều hơn một biến được tạo để lưu trữ một object, array, function. Những biến đó được trỏ đến địa chỉ đã được phân bổ tương tự trong bộ nhớ.

const arr1 = ['e', 's', 't', 'a', 'c', 'k', 's'];
const arr2 = arr1;
arr1[0] = 'a';
console.log(arr2); // ['a, 's', 't', 'a', 'c', 'k', 's']

Nhìn vào ví dụ trên, khi phần tử thứ nhất trong arr1 bị thay đổi, kéo theo đó thì arr2 cũng bị thay đổi theo. Tại sao vậy?

Khi arr1 được khai báo, bộ nhớ sẽ được cấp phát và một địa chỉ được lưu lại bởi nó. Sau đó arr2 được gán bằng arr1. Vì arr1 là một mảng thay vì tạo mới một bản sao của mảng đó, arr2 chỉ đơn giản là trỏ đến địa chỉ lưu arr1. Bằng cách đó, bất kỳ thay đổi nào được thực hiện trên arr1 cũng sẽ được thay đổi trên arr2 hoặc ngược lại bởi vì chúng trỏ đến cùng một vị trí.

Điều này cũng tương tự với object & function.

Những phiền toái

"Quên" mất việc biến đang tham chiếu

Đây cũng là trường hợp mình thấy phổ biến nhất. Khai báo một biến dựa trên một biến khác mà quên mất biến đó có tính chất reference.

Những bạn mới vào nghề sẽ rất dễ bị mắc lỗi này, vì thế khi biết được tính chất này thì hãy tránh khai báo một biến dựa trên một biến khác nhé, thay vào đó hãy sao chép nó ra.

const person1 = {
  name: 'Nguyễn Văn A',  
  age: 20,  
  address: {
    city: 'Hà Nội',  
    district: 'Cầu Giấy',  
  }
};
const person2 = { ...person1 };

Ở ví dụ trên mình vừa copy person1 sang person2. Hãy thử thay đổi giá trị của name hoặc ageperson1 thì person2 vẫn không bị thay đổi. Nhưng nếu thay đổi city hoặc district thì person2 vẫn bị thay đổi theo. Lý do là bởi address được khai báo với giá trị là object nên address vẫn có tính chất reference.

Cách copy object bằng cú pháp spread (...) ở trên hay nhiều cách copy khác như dùng Object.assign chỉ có thể sao chép "nông" (shallow) được đối tượng, để có thể sao chép được toàn bộ đối tượng lồng nhau như vậy mà tránh reference có thể dùng một trong ba cách sau:

Thứ nhất là dùng tổ hợp cú pháp JSON.parse & JSON.stringify. Đây là cách đơn giản & nhanh chóng nhất.

const person1 = {
  name: 'Nguyễn Văn A',  
  age: 20,  
  address: {
    city: 'Hà Nội',  
    district: 'Cầu Giấy',  
  }
};
const person2 = JSON.parse(JSON.stringify(person1));

Tuy nhiên đây lại là cách tồi nhất, do việc parse một string thành object hoàn toàn không tốt cho hiệu năng, chưa kể nếu như string đó là lớn.

Thứ hai là viết mã để thực hiện việc deep copy:

function deepCopy(obj) {
    if(typeof obj !== 'object' || obj === null) {
        return obj;
    }

    if(obj instanceof Date) {
        return new Date(obj.getTime());
    }

    if(obj instanceof Array) {
        return obj.reduce((arr, item, i) => {
            arr[i] = deepCopy(item);
            return arr;
        }, []);
    }

    if(obj instanceof Object) {
        return Object.keys(obj).reduce((newObj, key) => {
            newObj[key] = deepCopy(obj[key]);
            return newObj;
        }, {})
    }
}

Và cuối cùng, sử dụng thư viện có sẵn các hàm deepCopy như lodash, ramda... hoặc dùng package clone có sẵn trên npm.

Đặt một đối tượng dùng chung

Hình dung như bạn có một config để sử dụng làm mặc định nếu không tìm thấy những config riêng của chúng, bạn sẽ export ra một object chứa những config đó & thật tai hại nếu như trong quá trình sử dụng chúng, bạn vô tình thay đổi giá trị ở một nơi nào đó.

// file config.js
module.export = {
  appName: "estacks",  
  connection: {
    host: "0.0.0.0",  
    port: 80,  
}
// file app.js
const conf = require('./config.js');

let config = findConfig(); // null
if (!config) config = conf;
...  
// vô tình thay đổi conf
config.connection.port = 443;

Thì lúc này ở những file khác đang có import config.js thì connection.port đều bị chuyển thành 443 hết.

Giải pháp cho vấn đề này là hãy deep clone config ra trước khi sử dụng. Để tránh việc thay đổi chúng sẽ dẫn đến những lỗi ngớ ngẩn mà có thể mất cả tuần để debug :D.

Kết luận

Kiến thức về Object Reference chỉ đơn giản là chỉ một biến tham chiếu. Khi làm việc với biến tham chiếu bạn phải hết sức cẩn thận để không phải mắc những sai lầm như tôi ở trên nhé!

Cao cấp
Hello

Bí mật ngăn xếp của Blog

Là một lập trình viên, bạn có tò mò về bí mật công nghệ hay những khoản nợ kỹ thuật về trang blog này? Tất cả bí mật sẽ được bật mí ngay bài viết dưới đây. Còn chờ đợi gì nữa, hãy bấm vào ngay!

Là một lập trình viên, bạn có tò mò về bí mật công nghệ hay những khoản nợ kỹ thuật về trang blog này? Tất cả bí mật sẽ được bật mí ngay bài viết dưới đây. Còn chờ đợi gì nữa, hãy bấm vào ngay!

Xem tất cả

Đăng ký nhận thông báo bài viết mới

hoặc
* Bản tin tổng hợp được gửi mỗi 1-2 tuần, huỷ bất cứ lúc nào.

Bình luận (0)

Nội dung bình luận...
Bấm hoặc cuộn mạnh để sang bài mới