Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[2018-08-15]: shallowEqual(objA, objB) #2

Open
Lemonreds opened this issue Aug 15, 2018 · 0 comments
Open

[2018-08-15]: shallowEqual(objA, objB) #2

Lemonreds opened this issue Aug 15, 2018 · 0 comments
Labels

Comments

@Lemonreds
Copy link
Owner

函数源代码

const hasOwn = Object.prototype.hasOwnProperty

function is(x, y) {
  if (x === y) {
    return x !== 0 || y !== 0 || 1 / x === 1 / y
  } else {
    return x !== x && y !== y
  }
}

export default function shallowEqual(objA, objB) {

  if (is(objA, objB)) return true

  if (typeof objA !== 'object' || objA === null ||
      typeof objB !== 'object' || objB === null) {
    return false
  }
 const keysA = Object.keys(objA)
  const keysB = Object.keys(objB)

  if (keysA.length !== keysB.length) return false

  for (let i = 0; i < keysA.length; i++) {
    if (!hasOwn.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
      return false
    }
  }
  return true
}

函数用途

浅比较两个基本数据类型的值或者对象,在比较基本数据类型时,还会扩展一些比较,详情参考下方 is() 函数解读,而所谓的浅比较,是指在比较两个对象时,没有对对象内部嵌套的键值对进行进一步比较,因而会产生判断错误的情况,如:

let a = {
        keyA: 7,
        keyB: 8
 }
let b = {
        KeyA: 7,
        keyB: 8
}
shallowEqual(a,b)  // false

在浅比较 ab时,我们期待返回的是 true ,但函数返回的是 false, 因为函数是直接去比较 a 和 b这两个引用值是否相等,即 a === b,这样的比较一般都会返回 fasle ,除非 a 和 b 都指向同一个对象才会返回 true, 即 a = b = { key: 666 },所以浅比较在比较对象时,会产生误判。

代码解读 function is(x,y)

/**
 * 判断两个值是否相等
 * 基本数据类型的判断 boolean,string,number
 *  is(undefined,undefined  ): true
 *  is ( null ,null ) : true
 *  如果 x,y为对象类型,则只会简单比较二者的引用值是否相等.
 *
 * 函数中扩展了以下的两种判断
 *     在js中   +0 === -0 // 返回 true,但函数扩展后: 返回 false
 *     在js中   NaN === NaN //返回 false,但函数扩展后: 返回 true
 * 即
 *  is(+0,-0): false
 *  is ( NaN, NaN ) : true
 */

function is(x, y) {
  if (x === y) {
    // 处理为+0 != -0的情况
    // 1/+0 = Infinity 
    // 1/-0 = -Infinity
   //  Infinity === -Infinity  -> false
    return x !== 0 || y !== 0 || 1 / x === 1 / y
  } else {
    // 处理 NaN === NaN 的情况
    // (NaN === NaN)  ->  false
    return x !== x && y !== y
  }
}

可以看到,is实际上是 Object.is() 的polyfill,能对基本数据类型能进行精确的比较,而对于对象,则直接比较引用值是否相等。(这也是为什么shallowEqual函数不能适用于嵌套类型比较的原因)

代码解读 function shallowEqual(objA, objB)

export default function shallowEqual(objA, objB) {
  // 对基本数据类型进行比较
  if (is(objA, objB)) return true
  // is 函数无法判断的唯一情况是  2个参数均为对象
  if (typeof objA !== 'object' || objA === null ||
      typeof objB !== 'object' || objB === null) {
    return false
  }
  // 以下是对 对象 进行比较
 
  // keys 的个数不相等直接返回 false
  const keysA = Object.keys(objA)
  const keysB = Object.keys(objB)
  if (keysA.length !== keysB.length) return false

 /**
 * hasOwnProperty(props) : boolean
 * const hasOwn = Object.prototype.hasOwnProperty
 * 指示对象自身属性中是否具有指定的属性
 */

 // 遍历keysA 判断 objB 对象中是否有相同的键
 // 如果有相同的键则进一步比较该键的值是否相同
 // 如果这个相同的键的值是 对象 ,则没有对内容进行精确的比较,仅比较引用值是否相等  
  for (let i = 0; i < keysA.length; i++) {
    if (!hasOwn.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
      return false
    }
  }
  return true
}

总结

在react和react-router中都有用到这个函数,在 react的生命周期函数 shouldComponentUpdate(nextProps, nextState)中 说到react只会对 this.props 和 nextPropsthis.state 和 nextState 进行浅比较,而不适用于嵌套的对象。原因就是使用了此方法,源代码如下:

function shallowCompare(instance, nextProps, nextState) {
  return (
    !shallowEqual(instance.props, nextProps) ||
    !shallowEqual(instance.state, nextState)
  );
}

var ReactComponentWithPureRenderMixin = {
  shouldComponentUpdate: function(nextProps, nextState) {
    return shallowCompare(this, nextProps, nextState);
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant