5

我正在实现一个函数,它比较两个 JavaScript 对象的“深度”相等性。现在,这个函数的骨架如下所示:

function check_equal(actual, expected) {
    var stack = [];
    function check_equal_r(act, exp) {
        if (is_scalar(act) || is_scalar(exp)) {
            assert(act === exp);

        } else if (stack.indexOf(act) == -1) {
            assert(have_all_the_same_properties(act, exp));
            stack.push(act);
            for (var k of Object.getOwnPropertyNames(exp)) {
                check_equal_r(act[k], exp[k]);
            }
            stack.pop(act);

        } else {
            // ??? cyclic reference detected
        }
    }
    check_equal_r(act, exp);
}

问题是在它所说的地方放什么// ??? cyclic reference detected。理想情况下,我想说这些对象是深度相等的:

var a = {foo:1, bar:2, baz:null},
    b = {foo:1, bar:2, baz:null};
a.baz = a;
b.baz = b;

并且这些对象不是深度相等的:

var a = { car: 1, cdr: { car: 2, cdr: null } };
var b = { car: 1, cdr: { car: 2, cdr: null } };
a.cdr.cdr = a;
b.cdr.cdr = b.cdr;

笔记:

  • assert如果其参数为假,则抛出异常。
  • have_all_the_same_properties(x, y)如果和的getOwnPropertyNames列表不相同,则抛出异常。xy
  • is_scalar(x)实际上相同typeof x !== 'object'
  • 为了简洁起见,我在上面的代码中使用了 for-of 循​​环,但是 ES6 功能在实际运行的解释器中不可用。
4

2 回答 2

2

这是检查循环引用的算法的一个非常简单的扩展。它将exp与每个对象对应的对象保存act在一个单独的堆栈中,这样它就与在act其内部引用的任何对象具有相同的索引。

function is_scalar(v) {
    return typeof v !== 'object';
}

function have_all_the_same_properties(x, y) {
    var xprops = Object.getOwnPropertyNames(x),
        yprops = Object.getOwnPropertyNames(y);
    if (xprops.length === yprops.length) {
        return xprops.every(function (prop) {
            return yprops.indexOf(prop) !== -1;
        });
    }
    return false;
}

function check_equal(actual, expected) {
    var stack = [];
    var expected_stack = [];
    function check_equal_r(act, exp) {
        if (is_scalar(act) || is_scalar(exp)) {
            return act === exp;
        } else {
            var i = stack.indexOf(act);
            if (i == -1) {
                if (have_all_the_same_properties(act, exp)) {
                    stack.push(act);
                    expected_stack.push(exp);
                    var res = Object.getOwnPropertyNames(exp).every(function (k) {
                        return check_equal_r(act[k], exp[k]);
                    });
                    expected_stack.pop();
                    stack.pop();
                    return res;
                } else {
                    return false;
                }
            } else {
                return expected_stack[i] === exp;
            }
        }
    }
    return check_equal_r(actual, expected);
}

var a = {foo:1, bar:2, baz:null},
    b = {foo:1, bar:2, baz:null};
a.baz = a;
b.baz = b;

console.log(check_equal(a, b));

var c = { car: 1, cdr: { car: 2, cdr: null } };
var d = { car: 1, cdr: { car: 2, cdr: null } };
c.cdr.cdr = c;
d.cdr.cdr = d.cdr;

console.log(check_equal(c, d));
于 2015-08-24T19:29:16.603 回答
0

克里斯的回答是正确的。我最近编写了一个用于深度相等检查的 util 函数,并且还需要涵盖循环依赖。这是 github ( https://github.com/ryancat/simple-deep-equal ) 上的代码,它也涵盖了 NaN 情况。

于 2018-01-14T04:30:21.073 回答