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

Underscore 里面比较两个值的相等 #38

Open
jyzwf opened this issue Jan 9, 2018 · 0 comments
Open

Underscore 里面比较两个值的相等 #38

jyzwf opened this issue Jan 9, 2018 · 0 comments

Comments

@jyzwf
Copy link
Owner

jyzwf commented Jan 9, 2018

    var eq, deepEq

    eq = function (a, b, aStack, bStack) {
        // 因为 0=== -0 
        // 所以,先判断 a !== 0 保证了,a和b 是不为0 的,
        // 其次,判断,a 和b 与 0 和 -0 的关系 
        // 1/0 == Infinite
        // 1/-0 == -Infinite
        if (a === b) return a !== 0 || 1 / a === 1 / b


        // a和b 中只要有一个是 undefined 或者 null ,就判为 false ,严格模式下
        if (a == null || b == null) return false;

        // NaN 情况
        // 如果 a 是NaN ,则判断 b 是不是 NaN
        if (a !== a) return b !== b

        var type = typeof a
        // 经过上面的if 语句,如果是基本类型,如果相等,那么前面就已经有了结论,不会跳到这里,
        // 这里是排除 基本数据类型的判断,凡是数据基本类型,到了这一步,表明两者并不相等
        // 以便下面的对象的深度比较
        if (type !== 'function' && type !== 'object' && typeof b != 'object') return false;

        // 接下来进行对象的深度比较
        return deepEq(a, b, aStack, bStack);

    }


    deepEq = function (a, b, aStack, bStack) {
        // 如果 a,b 是underscore 的子类,
        // 那么就比较 _wrapped 属性值
        // 也就是传进来的 obj
        if (a instanceof _) a = a._wrapped;
        if (b instanceof _) b = b._wrapped;

        var className = toString.call(a)
        // 先用Object.prototype.toString 方法判断是否属于同一类型
        if (className !== toString.call(b)) return false

        // 下面针对不同的情况进行讨论
        switch (className) {
            case '[object RegExp]':
            case '[object String]':
                // new String
                // 正则和字符串的则转换为字符串来比较
                return '' + a === '' + b;

            case '[object Number]':
                // 如果+a !== +a 那么a = NaN
                // 此时判断 b 是否是 NaN
                if (+a !== +a) return +b !== +b;
                // 将 a 转换为  基本类型
                // 如果 a 为 0,判断 1 / +a === 1 / b
                // 否则判断 +a === +b
                return +a === 0 ? 1 / +a === 1 / b : +a === +b;


                // 直接将 Date 和 Boolean  转化为 数字比较
            case '[object Date]':
            case '[object Boolean]':
                return +a === +b;

                /** 
                 * var a = Symbol(1)
                    var b  = Symbol(1)
                    a === b  -> false

                    所以此时比较 时应该就比较 传入 Symbol 的参数

                    var a = Symbol(1)
                    var b  = Symbol(1)      
                    Symbol.prototype.valueOf.call(b) === Symbol.prototype.valueOf.call(a)  // false
                    Symbol.prototype.toString.call(b) === Symbol.prototype.toString.call(a) // true

                    // 这里好像判断失误了??????????????????????????????????????
                 */
            case '[object Symbol]':
                return SymbolProto.valueOf.call(a) === SymbolProto.valueOf.call(b);
        }

        // 判断是否是数组
        var areArrays = className === '[object Array]';
        if (!areArrays) { // 如果不是数组
            // 此时 typeof a| b 是函数。。这样两个函数不管如何直接 false
            if (typeof a != 'object' || typeof b != 'object') return false;

            // Objects with different constructors are not equivalent, but `Object`s or `Array`s
            // from different frames are.
            // 如果 a 和 b 有着不同的构造函数不一定是不等,如 Object 和 Array 如果在不同的 iframes 的时候
            var aCtor = a.constructor,
                bCtor = b.constructor;
            // aCtor !== bCtor 说明 两者的构造函数不同
            // _.isFunction(aCtor) 保证 能使用 instanceof 来进行判断
            // ('constructor' in a && 'constructor' in b) 是防止如下情况:
            /* var attrs = Object.create(null);
            attrs.name = "Bob";
            eq(attrs, {
                name: "Bob"
            }); 
            // 这两个对象应该是相等的
            */
            if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor &&
                    _.isFunction(bCtor) && bCtor instanceof bCtor) &&
                ('constructor' in a && 'constructor' in b)) {
                return false;
            }
        }


        aStack = aStack || [];
        bStack = bStack || [];

        var length = aStack.length;
        while (length--) { // 逐个比较其值    第一次时 length = 0 这里不会执行
            if (aStack[length] === a) return bStack[length] === b
        }

        aStack.push(a)
        bStack.push(b)

        if (areArrays) { // 如果是数组
            length = a.length
            if (length !== b.length) return false // 数组长度不等,自然两者不相等

            while (length--) { // 递归比较a和b 的一个个子元素,层层剥茧,只要有一个不同,就是false
                if (!eq(a[length], b[length], aStack, bStack)) return false
            }
        } else { // 是纯对象情况
            var keys = _.keys(a),
                key // 获取a 的所有 键
            length = keys.length
            // 键值长度不等,自然不等
            if (_.keys(b).length !== length) return false;
            while (length--) {
                // Deep compare each member
                key = keys[length];
                // 先看 b 中是否有这个键,有的话,再将a 和 b 对应这个键的键值进行递归比较 
                if (!(_.has(b, key) && eq(a[key], b[key], aStack, bStack))) return false;
            }
        }

        aStack.pop();
        bStack.pop();
        return true;

    }


    _.isEqual = function (a, b) {
        return eq(a, b);
    };

里面参考了JavaScript 中是如何比较两个元素是否 "相同" 的

不过其中也有些疑问,在上面的参考链接里面提出来了问题,可以移步到上面地址

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant