精讀《如何比較 Object 對象》

1 引言

Object 類型的比較是很是重要的基礎知識,經過 How to Compare Objects in JavaScript 這篇文章,咱們能夠學到四種對比方法:引用對比、手動對比、淺對比、深對比。javascript

2 簡介

引用對比

下面三種對比方式用於 Object,皆在引用相同是才返回 true前端

  • ===
  • ==
  • Object.is()
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

手動對比

寫一個自定義函數,按照對象內容作自定義對比也是一種方案:java

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

若是要對比的對象 key 很少,或者在特殊業務場景須要時,這種手動對比方法其實仍是蠻實用的。git

但這種方案不夠自動化,因此纔有了淺對比。github

淺對比

淺對比函數寫法有不少,不過其效果都是標準的,下面給出了一種寫法:redux

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;
}

能夠看到,淺對比就是將對象每一個屬性進行引用對比,算是一種性能上的平衡,尤爲在 redux 下有特殊的意義。數組

下面給出了使用例子:微信

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

若是對象層級再多一層,淺對比就無效了,此時須要使用深對比。函數

深對比

深對比就是遞歸對比對象全部簡單對象值,遇到複雜對象就逐個 key 進行對比,以此類推。性能

下面是一種實現方式:

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";
}

能夠看到,只要遇到 Object 類型的 key,就會遞歸調用一次 deepEqual 進行比較,不然對於簡單類型直接使用 !== 引用對比。

值得注意的是,數組類型也知足 typeof object === "object" 的條件,且 Object.keys 能夠做用於數組,且 object[key] 也可做用於數組,所以數組和對象均可以採用相同方式處理。

有了深對比,不再用擔憂複雜對象的比較了:

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

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

但深對比會形成性能損耗,不要小看遞歸的做用,在對象樹複雜時,深對比甚至會致使嚴重的性能問題。

3 精讀

常見的引用對比

引用對比是最經常使用的,通常在作 props 比較時,只容許使用引用對比:

this.props.style !== nextProps.style;

若是看到有深對比的地方,通常就要有所警覺,這裏是真的須要深對比嗎?是否是其餘地方寫法有問題致使的。

好比在某處看到這樣的代碼:

deepEqual(this.props.style, nextProps.style);

多是父組件一處隨意拼寫致使的:

const Parent = () => {
  return <Child style={{ color: "red" }} />;
};

一個只解決局部問題的同窗可能會採用 deepEqual,OK 這樣也能解決問題,但一個有全局感的同窗會這樣解決問題:

this.props.style === nextProps.style;
const Parent = () => {
  const style = useMemo(() => ({ color: "red" }), []);
  return <Child style={style} />;
};

從性能上來看,Parent 定義的 style 只會執行一次且下次渲染幾乎沒有對比損耗(依賴爲空數組),子組件引用對比性能最佳,這樣的組合必定優於 deepEqual 的例子。

常見的淺對比

淺對比也在判斷組件是否重渲染時很經常使用:

shouldComponentUpdate(nextProps) {
  return !shallowEqual(this.props, nextProps)
}

緣由是 this.props 這個對象引用的變化在邏輯上是無需關心的,由於應用只會使用到 this.props[key] 這一層級,再考慮到 React 組件生態下,Immutable 的上下文保證了任何對象子屬性變化必定致使對象總體引用變化,能夠放心的進行淺對比。

最少見的就是手動對比和深對比,若是你看到一段代碼中使用了深對比,大機率這段代碼能夠被優化爲淺對比。

4 總結

雖然今天總結了 4 種比較 Object 對象的方式,但在實際項目中,應該儘量使用引用對比,其次是淺對比和手動對比,最壞的狀況是使用深對比。

討論地址是: 精讀《如何比較 Object 對象》》· Issue #258 · dt-fe/weekly

若是你想參與討論,請 點擊這裏,每週都有新的主題,週末或週一發佈。前端精讀 - 幫你篩選靠譜的內容。

關注 前端精讀微信公衆號

版權聲明:自由轉載-非商用-非衍生-保持署名( 創意共享 3.0 許可證
相關文章
相關標籤/搜索