2

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?

1 Answers1

1

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:

  1. a.a is evaluated to find out what function will be called later in step 3.
  2. b() is evaluated since it's a function argument and its return value becomes the actual argument to whatever a.a evaluated to.
  3. Whatever 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:

  1. Let ref be the result of evaluating MemberExpression.
  2. 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.

Community
  • 1
  • 1
Paul
  • 139,544
  • 27
  • 275
  • 264
  • Which means that if `a.a` is not a function, then no matter what happens while evaluating its arguments, reference error must be thrown (except if evaluation itself throws an error)? Well, thats pretty weird. I thought JavaScript is designed to avoid unneccesary calculations like `f1() && f2()` if `f1()` evaluates to false, then `f2()` will not be executed. What is the logic of evaluating arguments of a function reference which we surely know we cannot call? –  Dec 06 '16 at 23:34
  • @manga171 It's arguments could have side effects, like `b()` does. The spec focuses on correctness more than optimization and the short-circuit evaluation of `f1() && f2()` can also have correctness implications, for instance `isReady() && go()` is guaranteed not to call `go()` if `isReady()` return `false`, there is no such benefit from not evaluating function arguments before calling the function; The opposite question is more interesting: What is the logic of evaluating the function reference before evaluating the arguments. – Paul Dec 06 '16 at 23:49
  • @manga171 The answer to that is probably that the spec is not perfect and making changes that affect existing code is a big deal. It is easy to show that `Let argList be ? ArgumentListEvaluation(Arguments).` always happens, so it would be possible to pull it out of any conditions and make it the first thing that happens when a function is evaluated, but it would change the behavior of the code in your question since it would never throw an exception; I think it would output `0undefined`. – Paul Dec 06 '16 at 23:55
  • @Paulpro The function reference is evaluated before the arguments to get left-to-right evaluation behaviour. Why the type error isn't thrown before the arguments are evaluated is a good question though. – Bergi Dec 06 '16 at 23:55
  • @Bergi I think the TypeError being thrown after the arguments are evaluated makes sense just because it is consistent with what would happen if the function itself throws an error immediately after being called; any side-effects caused by evaluated the arguments are applied. – Paul Dec 06 '16 at 23:59