Thật đơn giản để so sánh các kiểu dữ liệu nguyên thủy (Primitive data types)  trong Javascript. Toán tử == trả về true nếu các toán hạng bằng nhau, toán tử === trả về true nếu các toán hạng bằng nhau và cùng kiểu.

1 == '1'; // => true
1 === '1'; // => false

Tuy nhiên đối với object, sẽ có một chút khó khăn vì chúng là dữ liệu có cấu trúc. Trong bài này mình sẽ cùng các bạn đi tìm hiểu những cách so sánh 2 object trong javascript. Bắt đầu thôi :mrgreen:

1. So sánh bằng tham chiếu (Referential equality)

Javascript cung cấp 3 cách so sánh bằng tham chiếu 2 object:

  • Toán tử bằng chính xác ===
  • Toán tử bằng ==
  • Hàm Object.is()

Khi thực hiện so sánh các object bằng bất kì cách nào phía trên, kết quả sẽ là true nếu 2 giá trị được so sánh có cùng vùng nhớ. Đây chính là so sánh bằng tham chiếu.

Cùng đi qua ví dụ về so sánh bằng tham chiếu nhé.

const hero1 = {
  name: 'Batman'
};
const hero2 = {
  name: 'Batman'
};

hero1 === hero1; // => true
hero1 === hero2; // => false

hero1 == hero1; // => true
hero1 == hero2; // => false

Object.is(hero1, hero1); // => true
Object.is(hero1, hero2); // => false

Các bạn có thể xem hình dưới đây, add_1(object) đại diện cho vùng nhớ mà hero1 tham chiếu đến, add_1(name) đại diện cho vùng nhớ mà hero1.name tham chiếu đến.

So sánh object

Sơ đồ vùng nhớ

hero1 === hero1true bởi vì cả 2 object đều trỏ đến 1 vùng nhớ

hero1 === hero2false bởi vì hero1hero2 trỏ đến 2 vùng nhớ khác nhau dù cho chúng có cấu trúc tương đồng và giá trị name đều là batman.

Phép so sánh bằng tham chiếu rất hữu ích khi bạn muốn so sánh tham chiếu của 2 giá trị, nhưng trong nhiều trường hợp thì bạn cần so sánh giá trị thực sự của 2 object chẳng hạn: các thuộc tính và giá trị bên trong nó. Vậy thì xem cách bên dưới coi thử giúp được gì không nhé.

2. So sánh thủ công (Manual comparison)

Đây là cách so sánh khá rõ ràng và dễ hiểu. Ví dụ function isHeroEqual() dưới đây so sánh 2 object hero

function isHeroEqual(object1, object2) {
  return object1.name === object2.name;
}

const hero1 = {
  name: 'Batman'
};
const hero2 = {
  name: 'Batman'
};
const hero3 = {
  name: 'Joker'
};

isHeroEqual(hero1, hero2); // => true
isHeroEqual(hero1, hero3); // => false

isHeroEqual() truy cập đến thuộc tính name của cả 2 object và thực hiện so sánh 2 giá trị của chúng.

Nếu object có vài thuộc tính đơn giản thì chúng ta có thể viết function so sánh như isHeroEqual(). Cách này đem lại hiệu suất tốt nhưng nếu object lớn với nhiều thuộc tính chồng lên nhau thì đây không phải là cách hữu hiệu, vì code sẽ rất dài và khó đọc.

3. So sánh nông (Shallow equality)

Trong quá trình so sánh nông, các object của bạn sẽ được lấy các thuộc tính (bằng cách dùng Object.keys()), sau đó sẽ kiểm tra giá trị của các thuộc tính đó bằng cách so sánh tham chiếu

Đây là cách so sánh nông hoạt động

function shallowEqual(object1, object2) {
  const keys1 = Object.keys(object1);
  const keys2 = Object.keys(object2);

  if (keys1.length !== keys2.length) {
    return false;
  }

  for (let key of keys1) {
    if (object1[key] !== object2[key]) {
      return false;
    }
  }

  return true;
}

Bên trong function phía trên, keys1keys2 là các mảng chứa danh sách tên thuộc tính của object1object2.

Vòng lặp for sẽ lặp qua từng key và thực hiện phép so sánh tham chiếu bằng toán tử không bằng chính xác object1[key] !== object2[key].

Cùng mình xem kết quả so sánh dưới nhé

const hero1 = {
  name: 'Batman',
  realName: 'Bruce Wayne'
};
const hero2 = {
  name: 'Batman',
  realName: 'Bruce Wayne'
};
const hero3 = {
  name: 'Joker'
};

shallowEqual(hero1, hero2); // => true
shallowEqual(hero1, hero3); // => false

Ừm… Không có gì phải thắc mắc ở đây, nhưng hãy xem tiếp ví dụ phía dưới nữa nhé

const hero1 = {
  name: 'Batman',
  address: {
    city: 'Gotham'
  }
};
const hero2 = {
  name: 'Batman',
  address: {
    city: 'Gotham'
  }
};

shallowEqual(hero1, hero2); // => false
So sánh Object Javascript

Sơ đồ vùng nhớ

Lần này thì cả hero1hero2 có nội dung y hệt nhau nhưng kết quả so sánh lại trả về là false. Điều này xảy ra bởi vì giá trị hero1.addresshero2.address không còn là kiểu dữ liệu nguyên thủy  nữa, nó đã trở thành object, vì thế javascript sẽ so sánh tham chiếu của hero1.addresshero2.address. Lúc này hero1.addresshero2.address thuộc 2 vùng nhớ khác nhau.

May mắn là cho ta vẫn còn một cách so sánh nữa, đó là so sánh sâu, nó sẽ giúp chúng ta so sánh đầy đủ nội dung bên trong của object.

4. So sánh sâu (Deep equality)

So sánh sâu thì khá tương đồng với so sánh nông, nhưng thay vì so sánh nông chỉ so sánh một cấp thì so sánh sâu sẽ so sánh nhiều cấp, nó sẽ thực hiện so sánh toàn bộ thuộc tính của object. Với phép so sánh sâu này thì bạn có thể hiểu là nếu nội dung 2 object y hệt nhau thì nó sẽ bằng nhau.

Cùng xem cách mà so sánh sâu hoạt động nhé

function deepEqual(object1, object2) {
  const keys1 = Object.keys(object1);
  const keys2 = Object.keys(object2);

  if (keys1.length !== keys2.length) {
    return false;
  }

  for (const key of keys1) {
    const val1 = object1[key];
    const val2 = object2[key];
    const areObjects = isObject(val1) && isObject(val2);
    if (
      areObjects && !deepEqual(val1, val2) ||
      !areObjects && val1 !== val2
    ) {
      return false;
    }
  }

  return true;
}

function isObject(object) {
  return object != null && typeof object === 'object';
}

Như bạn thấy bên trên, ta thực hiện đệ quy để có thể lặp hết vào trong các thuộc tính của object. Cùng xem ví dụ thử nhé

const hero1 = {
  name: 'Batman',
  address: {
    city: 'Gotham'
  }
};
const hero2 = {
  name: 'Batman',
  address: {
    city: 'Gotham'
  }
};

deepEqual(hero1, hero2); // => true

So sánh sâu sẽ giúp ta xác định một cách chính xác 2 object có thực sự bằng nhau về mặt nội dung hay không.

Đọc thêm:

Để thực hiện so sánh sâu, mình khuyên sử dụng

5. Tóm lại

Bài hôm nay khá đơn giản, hy vọng bài viết của mình giúp các bạn hiểu được cách so sánh các object trong javascript. Chúc các bạn cuối tuần vui vẻ 😆 , nếu có gì thắc mắc có thể comment phía bên dưới, mình sẽ giải đáp :mrgreen:

Tham khảo: https://dmitripavlutin.com/how-to-compare-objects-in-javascript/