I am learning JavaScript, but there are a lot of things I cannot understand. At one online JavaScript quiz, the following question appeared:
What will the following JavaScript code log to console:
const a = {};
const b = () => a.a = () => {};
const c = c => '' + c + a.a(b());
const d = console.log.bind(console);
const e = (e => () => d(c(e++)))(0);
try{
e();
}catch(a){
e();
}
It took me some time to understand what each variable (here constant) stands for. I started analyzing the code from the e()
inside try
block. So, e
represents a closure, which means that function d
will be called with argument c(0)
and e
will become 1
. As I understood, here d
basically represents the console.log
function (but I cannot figure out why did they use bind
?).
For now, I know that first will be executed c(0)
and then result loged to console, right? Lets take a look at function c
. It returns first argument converted to string and concatenated result of a.a(b())
. Ok, so a.a(b())
will be executed first, am I right? But, the problem is because a.a
is not a function, it is undefined, so error will be thrown and we go to catch
.
Now, in catch
block everything should be the same, so a.a
is still not a function and reference error should be thrown. But, it surprised me when I saw that no error is thrown, but console actually logs 1undefined
. Why? How?
Ok, after a bit of thinking, I realized that maybe when calling a.a(b())
maybe b()
is executed first. Following my supposition, then function b
asign reference to a function which does nothing to property a
of object a
, right? But, then a.a
IS a function and it will be executed in try
block and 0undefined
will be logged.
However, none of these two suppositions are correct. The main question here is what is executed first? If we call someObject.propertyWhichIsNotAFunction(somethingWhichMakesItAFunction)
, what will happen? What is executed first? It seems that in try
block one thing is executed first and in catch
other thing. It really makes no sense to me. Any explanations?
This is very similar to the cause of Why is the value of foo.x undefined in foo.x = foo = {n: 2}? , just a little bit trickier since it is relying on when a TypeError is thrown in the execution.
Basically what happens is:
a.a
is evaluated to find out what function will be called later in step 3.b()
is evaluated since it's a function argument and its return
value becomes the actual argument to whatever a.a
evaluated to.a.a
evaluated to in step 1 is executed with the return value of b()
as its argument.The relevant part of the spec is here: https://www.ecma-international.org/ecma-262/7.0/index.html#sec-function-calls
Notice that the first thing that happens when a function is called is:
- Let ref be the result of evaluating MemberExpression.
- Let func be ? GetValue(ref).
That means that a.a
is evaluated and the function it refers to is called func
. The first time around a.a
evaluates to undefined
, so func
is undefined
and a TypeError will be thrown at step 2 of https://www.ecma-international.org/ecma-262/7.0/index.html#sec-evaluatedirectcall , which is after ArgumentListEvaluation(arguments)
, which calls b()
and assigns a new value to a.a
, but doesn't after the value of func
.