Đô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
  • Đây là bài viết thứ 366, có nghĩa là mình đã duy trì được việc mỗi ngày một bài trong chuyên mục Threads 1 năm rồi đấy mọi người. Tuy rằng không phải ngày nào cũng viết vì nhiều hôm mình bận, quên thì hôm sau là lên bài bù, mục đích là để cam kết với độc giả, ấy vậy mà quay đi quay lại đã một năm trôi qua rồi. Nhanh thật 😃

    À mai, kia, ngày kìa nữa mình đi du lịch nên chắc không lên bài cho mọi người được. Về rồi mình lên sau nhé 😅. Cảm ơn!

    » Xem thêm
  • Hơn 1 tuần nay mình không đăng bài, không phải không có gì để viết mà đang tìm cách để phân phối nội dung có giá trị hơn trong thời đại AI đang bùng nổ mạnh mẽ như thế này.

    Như từ hồi đầu năm đã chia sẻ, số lượng người truy cập vào trang blog của mình đang dần ít đi. Khi xem thống kê, lượng người dùng trong 6 tháng đầu năm 2025 đã giảm 30% so với cùng kì năm ngoái, 15% so với 6 tháng cuối năm 2024. Như vậy một sự thật là người dùng đang rời bỏ dần đi. Nguyên nhân do đâu?

    Mình nghĩ lý do lớn nhất là thói quen của người dùng đã thay đổi. Họ tìm thấy blog chủ yếu qua các công cụ tìm kiếm, trong đó lớn nhất là Google. Gần 1/2 số lượng người dùng quay trở lại blog mà không cần thông qua bước tìm kiếm. Đó là một tín hiệu đáng mừng nhưng vẫn không đủ để tăng lượng người dùng mới. Chưa kể giờ đây, Google đã ra mắt tính năng AI Search Labs - tức là AI hiển thị luôn nội dung tổng hợp khi người dùng tìm kiếm, điều đó càng khiến cho khả năng người dùng truy cập vào trang web thấp hơn. Một điều thú vị là khi Search Labs được giới thiệu, thì các bài viết bằng tiếng Anh đã soán ngôi trong bảng xếp hạng truy cập nhiều nhất.

    Một bài viết của mình thường rất dài, có khi lên đến cả 2000 chữ. Mà để viết ra được một bài như thế tốn nhiều thời gian. Nhiều bài viết ra chẳng có ai đọc là điều bình thường. Mình biết và chấp nhận vì không phải ai cũng gặp phải vấn đề đang nói đến. Viết đối với mình như một cách để rèn luyện sự kiên nhẫn và cả tư duy. Viết ra mà giúp được cả ai đó là một điều tuyệt vời.

    Vậy nên mình đang nghĩ sẽ tập trung vào nội dung ngắn và trung bình để viết được nhiều hơn. Nội dung dài chỉ khi muốn viết chi tiết hoặc đi sâu về một chủ đề nào đó. Nên là đang tìm cách thiết kế lại trang blog. Mọi người cùng chờ nha 😄

    » Xem thêm
  • CloudFlare đã giới thiệu tính năng pay per crawl để tính phí cho mỗi lần AI "cào" dữ liệu trên trang web của bạn. Là sao ta 🤔?

    Mục đích của SEO là giúp các công cụ tìm kiếm nhìn thấy trang web. Khi người dùng tìm kiếm nội dung mà có liên quan thì nó hiển thị trang web của bạn ra kết quả tìm kiếm. Điều này gần như là đôi bên cùng có lợi khi Google giúp nhiều người biết đến trang web hơn, còn Google thì được nhiều người dùng hơn.

    Bây giờ cuộc chơi với các AI Agents thì lại khác. AI Agents phải chủ động đi tìm kiếm nguồn thông tin và tiện thể "cào" luôn dữ liệu của bạn về, rồi xào nấu hay làm gì đó mà chúng ta cũng chẳng thể biết được. Vậy đây gần như là cuộc chơi chỉ mang lại lợi ích cho 1 bên 🤔!?

    Nước đi của CloudFlare là bắt AI Agents phải trả tiền cho mỗi lần lấy dữ liệu từ trang web của bạn. Nếu không trả tiền thì tôi không cho ông đọc dữ liệu của tôi. Kiểu vậy. Hãy chờ thêm một thời gian nữa xem sao 🤓.

    » 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

Tôi & khao khát "chơi chữ"

Bạn đã thử viết? Và rồi thất bại hoặc chưa ưng ý? Tại 2coffee.dev chúng tôi đã có quãng thời gian chật vật với công việc viết. Đừng nản chí, vì giờ đây chúng tôi đã có cách giúp bạn. Hãy bấm vào để trở thành hội viên ngay!

Bạn đã thử viết? Và rồi thất bại hoặc chưa ưng ý? Tại 2coffee.dev chúng tôi đã có quãng thời gian chật vật với công việc viết. Đừng nản chí, vì giờ đây chúng tôi đã có cách giúp bạn. Hãy bấm vào để trở thành hội viên 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...