修改后的deepEqual函数不起作用

时间:2017-02-06 02:22:56

标签: javascript for-loop recursion

我正在查看 Eloquent Javascript 练习中的deepEqual比较函数。我认为我可以通过将检查对象存在的if语句和值相等性检查移动到第一个for..in循环而不是第二个循环来改进建议解决方案。

我的理由是,如果对象没有匹配的属性或者它们的值不同而不是等待在第二个循环中这样做,它将允许检查更早失败。

此更改无效 This jsbin demonstrates the issue,这是代码:

function deepEqual(a, b){
  if(a === b) return true;
  if(a === null || typeof a !== "object" ||
     b === null || typeof b !== "object")
    return false;

  var pA = pB = 0;
  //console.log('OBJECT detected vals:',a,b);
  for(var p in a){
    pA++;
    //console.log('pA:'+pA, p, a, (p in b));
    // MOVED THE IF STATEMENT INTO THIS LOOP INSTEAD OF THE
    // SECOND LOOP BELOW
    if(!(p in b) || !deepEqual(a[p], b[p]))  
      return false;
  }

  for(var p in b){
    pB++;
    //console.log('pB:'+pB, p, b, (p in a));
    //if(!(p in a) || !deepEqual(a[p], b[p]))
      //return false;
  }

  return pA === pB;
}

var obj = {here: {is: "an"}, object: 2};
console.log(deepEqual(obj, obj));
// → true WORKS
console.log(deepEqual(obj, {here: 1, object: 2}));
// → false WORKS
console.log(deepEqual(obj, {here: {is: "an"}, object: 2}));
// → true, DOES NOT WORK, LOGS OUT FALSE...?

注释掉snippet中的console.log调用应该呈现以下输出:

console.log(deepEqual(obj, {here: {is: "an"}, object: 2}));
OBJECT detected vals: Object {here: Object, object: 2} Object {here: Object, object: 2}
pA:1 here Object {here: Object, object: 2} true
OBJECT detected vals: Object {is: "an"} Object {is: "an"}
pA:1 is Object {is: "an"} true
pB:1 is Object {is: "an"} true <-- why is this being called here, pA loop hasn't finished???
pA:2 object Object {here: Object, object: 2} true
pB:2 here Object {here: Object, object: 2} true
pB:3 object Object {here: Object, object: 2} true
  

从我所看到的情况来看,for..in参数的b在这里很早就开始了,并影响了循环的后续运行   价值。

我错过了什么?

我认为应该的方式

据我了解,第3个日志运行如下:

CALL:

deepEqual({here: {is: "an"}, object: 2},{here: {is: "an"}, object: 2})

  • (a === b)false
  • ab是对象,续
  • pApB设为0
  • 启动for..in循环

    1. pA为1,phere
      • here位于b,因此必须致电:
      • deepEqual({is: "an"}, {is: "an"})
      • 他们都是对象所以:
      • 启动for..in循环
        • pA为1,pis
          • here位于b,因此必须致电:
          • deepEqual("an", "an")
      • &LT;&LT;&LT;&LT;&LT;&LT;&LT;&LT;&LT;&LT;&LT;&LT;退货!!!
      • if失败,开始下一轮循环
    2. pA为2,pobject
      • object位于b,因此必须致电:
      • deepEqual(2, 2)
      • 他们都是对象所以:
      • 启动for..in循环
        • pA为1,pis
          • here位于b,因此必须致电:
          • deepEqual(a['is'], b['is'])
      • &LT;&LT;&LT;&LT;&LT;&LT;&LT;&LT;&LT;&LT;&LT;&LT;退货!!!
  • 此时pA为2

  • 剩下要做的就是迭代b

  • 中的道具
  • pB应为2

返回(pA === pB),与return 2===2相同,即return true

目前正在退出false

1 个答案:

答案 0 :(得分:3)

你被JavaScript的Implicit Globals

所困扰

问题在于这一行:

var pA = pB = 0;

当解构为多行时,它看起来像:

pB = 0;
var pA = pB;

这意味着pB未与var一起声明,因此是全局变量,而不是deepEqual的本地变量。这意味着它通过计算保留其值,因此具有错误的值。

您的函数返回false,因为对于具有2个属性的顶级对象级别,pB最终会使用值3而不是2,因为它会记住内部的计数level,有1个属性{ is: "an" }。另外两个测试是有效的,因为它们只检查单个级别的属性。

如果你使pB局部变量一切正常:

function deepEqual(a, b) {
  if (a === b) return true;
  if (a === null || typeof a !== "object" ||
    b === null || typeof b !== "object")
    return false;

  // make both variables local
  var pA = 0;
  var pB = 0;
  for (var p in a) {
    pA++;
    if (!(p in b) || !deepEqual(a[p], b[p]))
      return false;
  }

  for (var p in b) {
    pB++;
  }

  return pA === pB;
}

var obj = {here: {is: "an"}, object: 2};
console.log(deepEqual(obj, {here: {is: "an"}, object: 2}));

这是related question值得一试。