精读《如何比较 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 许可证
相关文章
相关标签/搜索