Can this type checks with "object" be improved?

98 views Asked by At
if (typeof a !== "object" && typeof b !== "object") {
    return a == b;
}
... // check pairwise equality of object a & b using `for in`

Is it the same as

if (typeof a !== "object") {
    return a == b;
}

Is there any b with typeof b === "object" which would change the semantics?

Are there any horrible edge cases I should be aware of? Comparisons between an object and a native type which have a non-intuitive boolean equality or disequality? Including any bugs in browser (I mean you IE6!)

3

There are 3 answers

3
Nick Craver On BEST ANSWER

The second check is not quite the same as the first, no, simply because JavaScript is weakly typed so at the very least consider the ".toString() effect", as well as others. For example these would fail the first check, but pass in the second:

var a = "[object Object]";
var b = {};

Or, a bit simpler (showing a case you may want to consider...but this passes both checks):

var a = 0;
var b = "0";

One fix would be to do a value and type check with === which is a strict comparison operator, you get type checking as well...but I'm not entirely sure that's what you're after, since the current check is explicitly "not an object".

4
gion_13 On

it's not the same.
here's an example to understand why :

var a = "a string";
var b = new String("a string");
console.log(typeof a);//"string" !== "object"
console.log(typeof b);//"object" === "object"
console.log('"' + a + '" ' + (a==b?"==":"!=") + ' "' + b + '"');

the same thing can happen for numbers :

var a = 1;
var b = new Number(1);

your two if statements are not the same for the obvious reason.
you could "improve" your type check by using the instanceof operator to suite tour needs :

if ((typeof a !== "object" || a instanceof Number || a instanceof String) &&
    (typeof b !== "object" || b instanceof Number || b instanceof String))
    return a == b;
1
rsp On

Take a look at isEqual from Underscore.js. It "Performs an optimized deep comparison between the two objects, to determine if they should be considered equal." It works for all types of variables. This is how it's implemented:

  // Perform a deep comparison to check if two objects are equal.
  _.isEqual = function(a, b) {
    // Check object identity.
    if (a === b) return true;
    // Different types?
    var atype = typeof(a), btype = typeof(b);
    if (atype != btype) return false;
    // Basic equality test (watch out for coercions).
    if (a == b) return true;
    // One is falsy and the other truthy.
    if ((!a && b) || (a && !b)) return false;
    // Unwrap any wrapped objects.
    if (a._chain) a = a._wrapped;
    if (b._chain) b = b._wrapped;
    // One of them implements an isEqual()?
    if (a.isEqual) return a.isEqual(b);
    // Check dates' integer values.
    if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime();
    // Both are NaN?
    if (_.isNaN(a) && _.isNaN(b)) return false;
    // Compare regular expressions.
    if (_.isRegExp(a) && _.isRegExp(b))
      return a.source     === b.source &&
             a.global     === b.global &&
             a.ignoreCase === b.ignoreCase &&
             a.multiline  === b.multiline;
    // If a is not an object by this point, we can't handle it.
    if (atype !== 'object') return false;
    // Check for different array lengths before comparing contents.
    if (a.length && (a.length !== b.length)) return false;
    // Nothing else worked, deep compare the contents.
    var aKeys = _.keys(a), bKeys = _.keys(b);
    // Different object sizes?
    if (aKeys.length != bKeys.length) return false;
    // Recursive comparison of contents.
    for (var key in a) if (!(key in b) || !_.isEqual(a[key], b[key])) return false;
    return true;
  };

See the Underscore.js source code to see the rest of the functions used by this one.

It's easy to miss some edge cases so I would recommend using a well tested code like this one instead of reinventing the wheel.