我在查看Eloquent Javascript练习中的deepEqual
比较函数。我想我可以通过将检查对象存在性和值相等检查的if
语句移动到第一个for..in
循环而不是第二个来改进建议的解决方案。
我的理由是,如果对象没有匹配的属性或它们的值不同,而不是在第二个循环中等待,那么检查就会提前失败
更改无效 。这个jsbin演示了这个问题,这是代码:
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...?
注释掉代码段中的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
根据我所看到的,
b
参数的for..in
在这里很早就开始了,并通过正确的值
我错过了什么
我认为应该工作的方式
据我所知,第三个日志应该运行如下:
呼叫:
deepEqual({here: {is: "an"}, object: 2},{here: {is: "an"}, object: 2})
(a === b)
→CCD_ 8a
和b
是对象,继续- 将
pA
和pB
设置为0 启动
for..in
环路pA
为1,p
为here
here
在b
中,因此必须呼叫:deepEqual({is: "an"}, {is: "an"})
- 它们都是物体,所以去吧:
- 启动
for..in
循环pA
为1,p
为is
here
在b
中,因此必须呼叫:deepEqual("an", "an")
- <lt<lt<lt<lt<lt<lt;返回TRUE
if
失败,开始循环的下一次迭代:
pA
是2,p
是object
object
在b
中,因此必须呼叫:deepEqual(2, 2)
- 它们都是物体,所以去吧:
- 启动
for..in
循环pA
为1,p
为is
here
在b
中,因此必须拨打:deepEqual(a['is'], b['is'])
- <lt<lt<lt<lt<lt<lt;返回TRUE
此时
pA
为2剩下要做的就是迭代b 中的道具
pB
应为2
返回(pA === pB)
,与return 2===2
相同,后者为return true
它当前正在注销false
。
您已经被JavaScript的隐式全局咬了一口
问题是这条线:
var pA = pB = 0;
当分解成多行时,它看起来像:
pB = 0;
var pA = pB;
这意味着pB
没有用var
声明,因此是全局变量,而不是deepEqual
的局部变量。这意味着它通过计算保持其值,因此具有错误的值。
函数返回false
,因为具有2个属性的顶级对象级别的pB
最终会得到值3
,而不是2
,因为它会记住内部级别的计数,内部级别具有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}));
这里有一个相关的问题值得一看。