This code here is pretty convoluted for what's happening. It's not that complex it seems it's just throwing things together to be purposefully misleading. I suspect this is an interview question. I wouldn't class it as really good.
Let's break it down for ease:
Object equality
How is the output true
I'll remove the unneeded code to focus on the real thing. The code can be simplified to
var obj = {};
console.log( obj == obj );
It should be obvious why that would be true - you're comparing the same thing to itself. The result true
is expected regardless1 of the value of obj
.
One additional thing we can mention here is that objects are only equal to themselves. That's distinct from other primitive types2 where to values that are the same are also equal3. So two different objects will still be different even if they contain similar information:
console.log("foo" === "foo"); //true
console.log(42 === 42); //true
console.log({foo: "bar"} === {foo: "bar"}); //false
console.log({} === {}); //false
1 There is one exception - the value NaN
.
2 Except Symbols which are primitives but by design each represents a unique value. It's usually not worth discussing them unless specifically focusing on Symbols, hence they are just a footnote here.
3 I can't seem to formulate an explanation so it doesn't sound like a tautology.
Return values for function
why is there a need to explicitly returning the object as the new keyword creates an object and returns it automatically
This is the crux of the problem here. How the new
keyword operates.
Yes, new
will create a new object instance when called with a function. However, if the function returns a non-null object, then that will be used as the return value and will override the instance creation:
function Foo() {} // <-- no `return` statement
function Bar() {
return null; // <-- returns `null`
}
function Baz() {
return 42; // <-- returns a non-`null` but primitive value
}
function Quux() {
return { hello: "world" }; // <-- returns an object
}
const foo = new Foo();
console.log("typeof foo:", typeof foo);
console.log("foo instanceof Foo:", foo instanceof Foo);
console.log("foo:", foo);
const bar = new Bar();
console.log("typeof bar:", typeof bar);
console.log("bar instanceof Bar:", bar instanceof Bar);
console.log("bar:", bar);
const baz = new Baz();
console.log("typeof baz:", typeof baz);
console.log("bar instanceof Baz:", baz instanceof Baz);
console.log("baz:", baz);
const quux = new Quux();
console.log("typeof quux:", typeof quux);
console.log("quux instanceof Quux:", quux instanceof Quux);
console.log("quux:", quux);
Therefore if both A
and B
have return obj
in their bodies and obj
is an object, then calling new A()
is the same as calling A()
without the new
operator which is therefore the same as just obj
. It's analogous for B
.
var obj = {};
function A() { return obj; }
console.log( new A() == A() ); //true
console.log( A() == obj ); //true
console.log( new A() == obj ); //true
There is a slight curve ball here in that the task uses the abstract equality operator (==
) which used is called "loose equality". I'm calling it a "curve ball" since it works in some unintuitive ways. However, it's only slight because none of this applies here - ==
will produce inconsistent results when the two operands are different types since it will trigger abstract equality comparison. It's a common pitfall and hence it's standard to exclusively use strict equality comparison (===
) which is consistent. Here is a quick example of inconsistency with ==
:
const a = "";
const b = 0;
const c = " ";
//comparing a string and a number - string is converted to a number.
//empty string `a` converts to a zero
console.log(a == b); //true
//comparing a string and a number again.
//spaces are trimmed from `c` and an empty string is left. It's again a zero
console.log(b == c); //true
//comparing two strings. Same base type - no conversion needed.
//two strings with different content are not equal
console.log(a == c); //false
//the `==` operator is not transitive
However, the operands here are not only the same types but literally the same value, so ==
will work exactly like ===
.
Additional information
With all this explanation in place, it's possible that the question is just a setup. If the value of obj
is changed then everything changes. If obj
is anything other than an object (so, any primitive type, or null
), then calling the functions with new
will actually produce a new instance and thus the results would be different:
var obj = 42;
function A() { return obj; }
function B() { return obj; }
console.log( new A() == new B() );
I've discussed it above but just in short
- the function returns a non-object value
- calling it with
new
produces a new instance
- two different instances are never going to be equal
Removing the new
operator from one of the function calls will also produce false
, as A()
will now return the 42
while new B()
produces an object. The two would not be equal.
If the new
is removed from both the function calls, then the result is going to be the same for almost any value of obj
. Anything except NaN
.