40

Recently I have been asked this question in an interview.

 var a = 1;
 var b = [1];

What will a == b; return.

When I checked that on my chrome browser console I got this.

var a = 1;
var b = [1];
a == b;
true

I have also checked

var a = 1;
var b =(1);
a == b;
true

I know that b in an array of size 1. Does that mean that the size of array is assigned to b. I am really confused. Can anyone explain me the logic?

dippas
  • 58,591
  • 15
  • 114
  • 126
hmachahary
  • 474
  • 4
  • 12
  • 8
    I don't know why you would be surprised about that example using parentheses? If you wrote down the equation `(1) = 1` on a piece of paper, you would immediately say that the equation was true. Why do you think it would be otherwise when doing its equivalent in Javascript? – Some programmer dude May 04 '16 at 07:43
  • @JoachimPileborg: however `a` is undefined in this case. – Paul May 04 '16 at 07:44
  • Here is one good article http://rainsoft.io/the-legend-of-javascript-equality-operator/ – zangw May 04 '16 at 07:46
  • @Paul I'm not talking about the original interview question, but the last example where the OP does `var b = (1);`. – Some programmer dude May 04 '16 at 07:46
  • 1
    @Paul I'm pretty sure OP means `var i = 1;` to be `var a = 1;` It would be a trivial interview question otherwise. – timolawl May 04 '16 at 07:46
  • @Vucko: Exactly. Sometimes the best interview questions are those that make sure the interviewee is paying attention. We use them all the time in every interview. – Paul May 04 '16 at 07:49
  • 1
    @nisar That's a bad edit: We don't know if this is the actual interview question or not - only the OP can fix this, if indeed it's broken. See the above comments discussion – James Thorpe May 04 '16 at 07:58
  • 1
    @JamesThorpe I rolled back the edit for OP to edit him/herself. – timolawl May 04 '16 at 08:00
  • @Paul: I m sorry it was my typing mistake. It should be var a = 1; – hmachahary May 04 '16 at 08:24
  • and this is why you should always use `===` instead of `==` unless you _really_ know what you're doing... – Alnitak May 04 '16 at 10:37
  • 6
    This is the sort of interview question that makes me cross. What earthly practical use is this knowledge? – Bob Tway May 04 '16 at 14:12
  • There are plenty of weird things happening in js with the operators when the operands are of different type. see https://www.destroyallsoftware.com/talks/wat for more fun stuff, and http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3 for the full spec of the `==` operation – njzk2 May 04 '16 at 14:35
  • That's type coercion at work. ECMAScript standard can explain the logic in finer details. Read it. – Oleg V. Volkov May 04 '16 at 14:49
  • 3
    I agree Matt. If someone is bumping up across these quirks constantly to where they'd know the answer, I'd question how good they even are at programming. – Anna May 04 '16 at 15:26

3 Answers3

56

I didn't really understood from Rayon answer how valueOf and toString come into play when converting an object to a primitive value; so I dug into the ECMAScript 2015 specifications.

Warning: Long answer.

We want to check the expression 1 == [1].

Starting from the 12.10 Equality Operators we see that, after retrieving the expressions values, the last step is

  1. Return the result of performing Abstract Equality Comparison rval == lval

Abstract Equality Comparison is defined at chapter 7.2.12 Abstract Equality Comparison.

7.2.12 Abstract Equality Comparison
The comparison x == y, where x and y are values, produces true or false. Such a comparison is performed as follows:

  1. ReturnIfAbrupt(x).
  2. ReturnIfAbrupt(y).
  3. If Type(x) is the same as Type(y), then
    a. Return the result of performing Strict Equality Comparison x === y.
  4. If x is null and y is undefined, return true.
  5. If x is undefined and y is null, return true.
  6. If Type(x) is Number and Type(y) is String, return the result of the comparison x == ToNumber(y).
  7. If Type(x) is String and Type(y) is Number, return the result of the comparison ToNumber(x) == y.
  8. If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.
  9. If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
  10. If Type(x) is either String, Number, or Symbol and Type(y) is Object, then return the result of the comparison x == ToPrimitive(y).
  11. If Type(x) is Object and Type(y) is either String, Number, or Symbol, then return the result of the comparison ToPrimitive(x) == y.
  12. Return false.

The expression 1 == [1] falls under case 10.
So basically, as expected, the array [1] is converted into a value of primitive type.

ToPrimitive is define at 7.1.1 ToPrimitive ( input [, PreferredType] )

The abstract operation ToPrimitive takes an input argument and an optional argument PreferredType. The abstract operation ToPrimitive converts its input argument to a non-Object type.

I haven't included the full quotation since the only interesting, for this example, parts are:

  1. The PreferredType argument (actually an hint var) is converted from "default" (since it is not passed) to "number".
  2. OrdinaryToPrimitive is called with the same arguments.

E now the interesting part, OrdinaryToPrimitive do the following:

  1. Assert: Type(O) is Object
  2. Assert: Type(hint) is String and its value is either "string" or "number".
  3. If hint is "string", then
    a. Let methodNames be «"toString", "valueOf"».
  4. Else,
    a. Let methodNames be «"valueOf", "toString"».
  5. For each name in methodNames in List order, do
    a. Let method be Get(O, name).
    b. ReturnIfAbrupt(method).
    c. If IsCallable(method) is true, then
    ... i. Let result be Call(method, O).
    ... ii. ReturnIfAbrupt(result).
    ... iii. **If Type(result) is not Object, return result. **
  6. Throw a TypeError exception

So in order to convert [1] to a primitive value, the runtime try first to call valueOf. This method returns the array itself, which is an object so by 5.c.iii the method toString is called next.
This method returns the elements of the array as a comma-separated list, so it just returns the string "1".

So we are reduced comparing 1 == "1" which by the rules of Abstract Equality Comparison, point 6, means to convert "1" into the number 1 and than performing the trivial comparison 1 = 1.

The dubious reader is invited to check how Strict Equality Comparison is actually defined in the standard.


You can play with this conversions for better understanding them, here a sample playground HTML file

<html>
    <head><title>title</title></head>
    <body>
        <script>
            var old_valueOf = Array.prototype.valueOf;
            var old_toString = Array.prototype.toString;

            Array.prototype.valueOf = function(){ console.log("Array::valueOf"); return old_valueOf.apply(this); };
            Array.prototype.toString = function(){ console.log("Array::toString"); return old_toString.apply(this); };

            console.log(1 == [1]); //Array::valueOf, Array::toString, true 

            Array.prototype.valueOf = function(){ console.log("Array::valueOf"); return 2; };

            console.log(1 == [1]); //Array::valueOf, false 

            Array.prototype.valueOf = function(){ console.log("Array::valueOf"); return {}; };
            Array.prototype.toString = function(){ console.log("Array::toString"); return {} };

            console.log(1 == [1]); //Array::valueOf, Array::toString, Uncaught TypeError: Cannot convert object to primitive value
        </script>
    </body>
</html>
Margaret Bloom
  • 41,768
  • 5
  • 78
  • 124
52

If an object is compared with a number or string, JavaScript attempts to return the default value for the object. Operators attempt to convert the object to a primitive value, a String or Number value, using the valueOf and toString methods of the objects. If this attempt to convert the object fails, a runtime error is generated.[Ref]

var a = 1;
var b = [1];
//What is happening when `(a==b)`
//typeof a;   ==> number
//typeof b;  ==>object
//Object is converted to Primitive using `valueOf` and `toString` methods of the objects
var val = b.valueOf().toString();
console.log('Values after conversion is: ' + val + '  And typeof converted value is:  ' + typeof val);
//typeof val; ==> string
//a == b; will be evaluated as `true` because `'1' == 1` hence..
console.log(a == b); //'1'==1 ==> true

As converted value is of type String, When comparing a number and a string, the string is converted to a number value and then strict comparison is applied.

Jed Fox
  • 2,979
  • 5
  • 28
  • 38
Rayon
  • 36,219
  • 4
  • 49
  • 76
  • So it means `[1]` is converted to string `"1"` that is converted to integer `1`? Is that right? – Justinas May 04 '16 at 07:41
  • No - read the comments in the OP. – Paul May 04 '16 at 07:42
  • If you look more closely, a is undefined; the original value defined is actually **i**. – Paul May 04 '16 at 07:46
  • what does it mean `JavaScript attempts to return the default value for the object.` What is default value in above case? – ozil May 04 '16 at 08:08
  • The one thing that doesn't make sense to me is that you've directly casted the primitive value of b to a string in the line `var val = b.valueOf().toString();`, therefore `console.log(typeof val)` is going to surely return `string`? Am I missing something? – Paul May 04 '16 at 08:59
  • @Paul, That was just to demonstrate the the evaluation of `'1' == 1 ==> true`.. – Rayon May 04 '16 at 09:50
  • `Object.prototype.toString()` should be the only standard for type checking, as `typeof` is [flawed](https://bonsaiden.github.io/JavaScript-Garden/). I understand what you're doing though. – timolawl May 04 '16 at 15:39
13

This is due to the type of comparison being made.

In javascript, one can use either == or === for comparison. In the case of a triple equals, this is what's known as equality without type coercion, in other words, it's a strict comparison.

Equality with type coercion

Conversely, this means that using the double equals operand is equality with type coercion.

What does this mean?

Simply put, it means that javascript will use in-built methods in order to convert the value to a primitive type, ready for comparison. Specifically, those methods are .valueOf() and .toString().

Here are some examples:

0 == false   // true, auto type coercion
0 === false  // false, because they are of a different type
1 == "1"     // true, auto type coercion
1 === "1"    // false, because they are of a different type

Ergo:

1 == [1] // true
1 === [1] // false, because they are of a different type
gdgr
  • 1,435
  • 2
  • 15
  • 31
  • An explanation for the downvote would be much appreciated, I will happily ameliorate my answer if there's something which would add to its value. – gdgr May 04 '16 at 11:01
  • I didn't downvote, but I'm guessing it was due to only covering that type conversion is going on and not explaining how `[1]` becomes `1` when the type conversion happens. +1 though for showcasing the difference between `==` and `===` – JustWannaFly May 04 '16 at 16:03
  • I suppose if you're to take to the question literally, the OP did say 'how', however I thought that was possibly a language barrier issue and the question was actually more of a 'why', given that s/he was asked this by an interviewer whom I assume was probing for this exact knowledge. (More so I'd expect than _how_ javascript fundamentally handles this nuance.) Still, I thought the other answerers did a good job of explaining that and I didn't want to copy! Thanks for your feedback @jdkorv11 :) – gdgr May 04 '16 at 16:28