Khi so sánh 2 Object trong Javascript chăc hẳn bạn sẽ ngạc nhiên khi thấy 2 Object hoàn toàn giống nhau nhưng lại không bằng nhau.

Trong bài viết này ta sẽ cùng tìm hiểu về Object trong Javascript và xem cách Javascript so sánh 2 object như thế nào?

 

Bài toán đặt ra

Đầu tiên chúng ta cùng xem qua ví dụ dưới đây

 

const obj1 = {a: 1, b: 2};
const obj2 = {a: 1, b: 2};

 

Ta có 2 object là obj1 và obj2. Khi so sánh obj1 và obj2 kết quả sẽ trả về false mặc dù 2 object này hoàn toàn giống nhau.

 

console.log(obj1 === obj2); // false

 

Để tìm hiểu về vấn đề này, trước tiên ta phải xem cách mà Javascript khởi tạo một Object.

 

Khi tạo một object trong JavaScript, nó được lưu trữ trong bộ nhớ và một tham chiếu đến vùng nhớ đó được trả về. Do đó, khi ta so sánh 2 object, thực chất ta đang so sánh 2 tham chiếu tới 2 vùng nhớ khác nhau. Ngay cả khi 2 object có cùng cấu trúc và giá trị, nếu chúng được tạo ra tại các vùng nhớ khác nhau, thì 2 tham chiếu đến chúng sẽ khác nhau.

 

Vì vậy, khi sử dụng toán tử so sánh === hay == để so sánh 2 object trong JavaScript, ta chỉ kiểm tra xem chúng có cùng tham chiếu tới cùng một vùng nhớ hay không. Nếu chúng tham chiếu tới các vùng nhớ khác nhau, thì kết quả sẽ trả về false, ngay cả khi chúng có giá trị giống nhau.

 

Cách so sánh 2 Object

Trong một số trường hợp chúng ta sẽ muốn 2 object có cùng cấu trúc và giá trị thì khi so sánh phải bằng nhau. Do đó chúng ta có một số cách để so sánh 2 object.

 

    1. JSON.stringify()

Để so sánh nội dung của 2 object, ta có thể sử dụng các phương thức như JSON.stringify() để chuyển đổi object thành chuỗi JSON và so sánh chuỗi này.

Ví dụ, để so sánh nội dung của 2 object obj1obj2, ta có thể làm như sau:

console.log(JSON.stringify(obj1) === JSON.stringify(obj2)); // true

 

Với cách này ta có thể kiểm tra xem 2 object có cấu trúc và giá trị hoàn toàn giống nhau hay không. Tuy nhiêm trong một số trường hợp 2 object có cùng giá trị nhưng vị trí không giống nhau như ví dụ dưới đấy:

 

const obj3 = {
    id: 1,
    name: "Hello"
}

const obj4 = {
    name: "Hello",
    id: 1
}

 

Cả 2 object3 và objec4 đều có chung các giá trị nhưng lại bị đảo ngược vị trí. Khi sử dụng JSON.stringify() để chuyển object sang chuỗi JSON thì 2 object này sẽ không bằng nhau. Do đó kết quả sẽ bằng false

 

console.log(JSON.stringify(obj3) === JSON.stringify(obj4)) // false

 

    2. So sánh thủ công

Để so sánh 2 object phức tạp trong JavaScript ta có thể sử dụng một hàm đệ quy. Hàm này sẽ so sánh từng thuộc tính của 2 object và kiểm tra xem chúng có giống nhau hay không. Nếu một trong số các thuộc tính này là một object, ta sẽ tiếp tục sử dụng đệ quy để kiểm tra từng thuộc tính của object đó.

 

Dưới đây là một ví dụ về hàm so sánh 2 object phức tạp trong JavaScript:

 

function isObjectsEqual(obj1, obj2) {
  // Kiểm tra kiểu dữ liệu của 2 đối tượng
  if (typeof obj1 !== typeof obj2) {
    return false;
  }

  // Kiểm tra số lượng thuộc tính của 2 đối tượng
  if (Object.keys(obj1).length !== Object.keys(obj2).length) {
    return false;
  }

  // Kiểm tra từng thuộc tính của 2 đối tượng
  for (let key in obj1) {
    if (!obj2.hasOwnProperty(key)) {
      return false;
    }

    if (typeof obj1[key] === "object" && typeof obj2[key] === "object") {
      if (!isObjectsEqual(obj1[key], obj2[key])) {
        return false;
      }
    } else if (obj1[key] !== obj2[key]) {
      return false;
    }
  }

  return true;
}

 

Hàm này sẽ kiểm tra kiểu dữ liệu của 2 đối tượng, kiểm tra số lượng thuộc tính của 2 đối tượng, và kiểm tra từng thuộc tính của 2 đối tượng. Nếu một trong số các thuộc tính này là một object, ta sẽ tiếp tục sử dụng đệ quy để kiểm tra từng thuộc tính của object đó.

 

Ví dụ, để so sánh 2 object sau:

 

const obj1 = {
  a: 1,
  b: {
    c: 2,
    d: {
      e: 3
    }
  }
};

const obj2 = {
  a: 1,
  b: {
    c: 2,
    d: {
      e: 3
    }
  }
};

 

Ta có thể sử dụng hàm isObjectsEqual() để kiểm tra xem chúng có giống nhau hay không

 

console.log(isObjectsEqual(obj1, obj2)); // true

 

Lưu ý rằng hàm này không thể so sánh các thuộc tính không được liệt kê (non-enumerable properties) hoặc các thuộc tính của object được định nghĩa dưới dạng getter/setter.

 

3. Sử dụng thư viện

Việc so sánh 2 object cũng không hề đơn giản, do đó, hiện nay có rất nhiều thư viện Javascript hỗ trợ việc so sánh 2 object mà bạn có thể sử dụng, phổ biến như: Lodash, UnderscoreJS, Ramda,...

 

Để so sánh 2 object phức tạp bằng Lodash, bạn có thể sử dụng hàm isEqual() như sau:

 

const _ = require('lodash');

const obj1 = { 
    name: 'John',
    age: 30,
    address: {
        city: 'New York',
        country: 'USA'
    }
};

const obj2 = {
    name: 'John',
    age: 30,
    address: {
        city: 'New York',
        country: 'USA'
    }
};

console.log(_.isEqual(obj1, obj2)); // true

 

Trong ví dụ trên, ta sử dụng hàm isEqual() của Lodash để so sánh 2 object obj1obj2. Hàm này sẽ đệ quy so sánh từng thuộc tính của 2 object và trả về true nếu chúng giống nhau. Trong trường hợp này, kết quả trả về là true, vì 2 object này có cùng cấu trúc và giá trị.

 

Ngoài ra, Lodash còn cung cấp nhiều hàm khác để xử lý dữ liệu, như merge(), cloneDeep(), omit(),... để giúp bạn thực hiện các tác vụ phức tạp trên object và array.

 

Tổng kết

Trong JavaScript, ta không thể so sánh 2 object trực tiếp bằng toán tử so sánh === hay == vì chúng chỉ so sánh tham chiếu tới vùng nhớ, chứ không phải nội dung của object.

Để so sánh nội dung của 2 object, ta phải so sánh từng thuộc tính bằng đệ quy hoặc để đơn giản hóa mã code ta có thể sử dụng các thư viện hỗ trợ như Lodash, UnderscoreJS, hoặc Ramda. Trong đó, Lodash là một thư viện phổ biến và hỗ trợ nhiều tính năng xử lý dữ liệu khác nhau, bao gồm so sánh 2 object phức tạp bằng hàm isEqual().