Introduction
JavaScript is notoriously a simple programming language, but let’s be honest: it sucks!
Don’t get me wrong! I love programming in JavaScript, it gives extraordinary freedom in building things that runs everywhere, but at the cost that is so easy to write bad code. The reason falls back to its winding history, starting from its creation in 10 days by Brendan Eich. Wikipedia has an exhaustive entry on the subject, so I suggest you to check it.
Nevertheless after 20 years, a language with loads of imperfections, is going through its sixth standardization and is currently implemented on PCs, TVs, watches, cars, circuits and is, from time to time, adopted in place of Java in server environments. I remember back in the days when I was using JS to create random snowflakes on a webpage, now enterprise JS applications consists in thousands of hundreds lines of code and handle the business of billion different companies.
The Quiz
While browsing the web I stumbled upon one of the many JavaScript quizzes that challenge the visitors to guess the output of small functions. The quiz is quite challenging in some points, so I decided to write a blog post trying to explain each question, so you can reason more extensively on the answer.
Question 1
(function(){ return typeof arguments; })();
The first thing to note here is that this is an immediately-invoked function (or IIFE in short), it executes immediately as the interpreter parses it. Next we have to investigate what is typeof
, which is simply a keyword that returns the type of the next object, in this case arguments
.
Notice:
typeof
is not reliable. Use polyfills!
Finally, what is arguments
? If you have a basic understanding of JS you could say that the variable is not defined anywhere in the code and so it is undefined
, and typeof "undefined"
returns “string”. Otherwise, if you are coming from C-type languages you could say that arguments
matches argc
in C or Java, which is an array. However in JS this is always an object.
The arguments object is not an Array. It is similar to an Array, but does not have any Array properties except length.
However you should be aware that in JS arrays are also objects, so the answer would have been “object” in any case.
Question 2
var f = function g(){ return 23; };
typeof g();
In this case we have to investigate the difference between function declaration and function expression. So I suggest you to read this fantastic article by Angus Croll, which also explains what is hoisting in JS.
So what is going on here? Basically we are defining a variable f
for the function g
, which means that after the assignment the variable g
is no longer available in the scope. So executing g()
gives an error.
Question 3
(function(x){ delete x; return x; })(1);
Here we encounter another IIFE. This time the interesting part is the body of the function which receives 1 as parameter. What is the effect of delete
on x
and what is the return value? The common sense suggests that must be something like undefined
or more likely should throw an error. However as MDN points out
Unlike what common belief suggests, the delete operator has nothing to do with directly freeing memory […]
delete
is only effective on an object’s properties. It has no effect on variable or function names.
So, in the light of this explanation, the result is obviously 1.
Question 4
var y = 1, x = y = typeof x;
x;
This is actually really simple if you know of associativity works.
Assignment operators are right-associative
This means that we can read the code x = y = typeof x
from right to left in order to get the value of x
. But first let’s rewrite the code in a clearer form.
var y = 1;
var y = typeof x;
var x = y;
x;
We assign to y
the value 1, then in the second row, we assign the result of typeof x
to y
, overwriting the previous value. x
is undefined
so typeof undefined
is “undefined”. Finally the content y
is returned also to x
.
Question 5
(function f(f){ return typeof f(); })(function(){ return 1; });
Another interesting IIFE. The function f
receives a single argument (called again f
) which is defined as an anonymous function. Here there is an important thing to note: the function call f()
inside the function f
applies to the function argument, not to the external function f
. Why is this? Because JS has function-level variable scope so the argument f
of the function “shadows” the function name. To be clear, the following would be equal to the snippet above:
(function f(g){ return typeof g(); })(function(){ return 1; });
So what is the answer? The function f
invokes itself, with a simple function as argument, and in the body we have an instruction typeof
which applies to the return value of the function argument, that is 1. So the answer is number.
Question 6
var foo = {
bar: function() { return this.baz; },
baz: 1
};
(function(){
return typeof arguments[0]();
})(foo.bar);
This question is slightly harder because one must know how the this
operator works. As MDN points out
this
refers to the current context object
In the foo
object, when the bar
function is called, its this
is set to the object the method is called on. This means that if we call foo.bar()
we would get 1.
However calling the same exact function, using arguments[0]
, in the context of a IIFE would not return the previous result because this
now points to the window
object (or global object) and not to foo
. The result is then “undefined” because in the global object there is no property called baz
.
In JavaScript however, the Function.prototype
provides two useful methods for binding the desired object to the this
keyword. Those methods are call
and apply
and their use is quite popular in jQuery plugins.
Question 7
var foo = {
bar: function(){ return this.baz; },
baz: 1
}
typeof (f = foo.bar)();
This is a variation of the previous question. However the answer is still “undefined”. Can you see why?
Exactly! this
still points to the global object, therefore baz
is undefined.
Question 8
var f = (function f() { return "1"; }, function g() { return 2; })();
typeof f;
What? How are we supposed to interpret this code? (function f() { return "1"; }, function g() { return 2; })()
Let’s see how MDN describes the comma operator
The comma operator evaluates each of its operands (from left to right) and returns the value of the last operand.
So it’s clear that we can rewrite the first row as follows
var f = g();
The result of g()
is 2 and it is assigned to f
. Our answer is “number”, returned by typeof 2
.
Question 9
var x = 1;
if (function f(){}) {
x += typeof f;
}
x;
The
if
statement executes a statement if a specified condition is true. If the condition is false, another statement can be executed.
In this question the condition is the function f
, which is syntactically valid as a function expression but also as a function declaration. However the if statement takes an expression, which in this case is a function and functions in JS are always interpreted as truthy values. So we can rewrite the snippet as follows
var x = 1;
x += typeof f;
x;
Here is clearer that f
is undefined, so our answer is “1undefined”. Remember that in JS the +
operator is used to sum numeric values but also to concatenate strings.
Question 10
var x = [typeof x, typeof y][1];
typeof typeof x;
This one is really simple. We could solve it just by looking at the second row, because typeof x
yields always a string, independently by the value of x
, thus calling typeof
of string is obviously “string”.
Said so, the first row evaluates to var x = typeof y
, which is “undefined”;
Question 11
(function(foo){
return typeof foo.bar;
})({ foo: { bar: 1 } });
This question is tricky, but simple in reality, however it requires a bit of substitutions. We can rewrite everything in this form, equivalent to the previous
(function(baz){
return typeof baz.bar;
})({ foo: { bar: 1 } });
If we substitute the object { foo: { bar: 1 } }
with baz
, as a parameter of the IIFE, we note that in the body of the function we are trying to get the bar
parameter from an object that has only the foo
property. The answer is then “undefined”.
To access the bar
property inside foo
we should have used this notation baz.foo.bar
.
Question 12
(function f(){
function f(){ return 1; }
return f();
function f(){ return 2; }
})();
As you may imagine, this snippet requires you to know how function declaration hoisting works. If you read the article linked in Question 2 you should definitely be able to solve this one.
As a recap, function declarations are first hoisted to the top of the scope and then evaluated in order. So the code is equivalent the the following:
(function f(){
function f(){ return 1; }
function f(){ return 2; }
return f();
})();
f
refers in order to the top level anonymous function, then to the inside function returning 1 and finally to the second function returning 2. So when we call f
the value 2 is returned.
Question 13
function f(){ return f; }
new f() instanceof f;
First we have to understand how the new
operator works:
The new operator creates an instance of a user-defined object type or of one of the built-in object types that has a constructor function
When the code new f()
is executed, the following things happen, accordingly to MDN:
- A new object is created, inheriting from
f.prototype
. - The constructor function
f
is called with the specified arguments (in this case we have none) and this bound to the newly created object. - The object returned by the constructor function becomes the result of the whole new expression. If the constructor function doesn’t explicitly return an object, the object created in step 1 is used instead.
Consider the following code:
function f(){ return f; }
var g = new f();
g === f;
Here g
is equal to f
because the constructor of f
returns f
itself.
So we know that new f() === f
, now the missing point is what f instanceof f
would yield.
The
instanceof
operator tests whether an object has in its prototype chain the prototype property of a constructor.
We know that f
is a function because it is an instance of the Function object. This means that f instanceof Function === true
which proves directly that f instanceof f
is false.
Question 14
with (function(x, undefined){}) length;
I failed this question so horribly, mainly because I learned in the past to avoid the with
statement, as MDN points out wisely
Use of the with statement is not recommended, as it may be the source of confusing bugs and compatibility issues.
Using with is not recommended, and is forbidden in ECMAScript 5 strict mode. The recommended alternative is to assign the object whose properties you want to access to a temporary variable.
Let’s see what are we dealing with here:
The
with
statement extends the scope chain for a statement.
The following example explains clearly what it does
var a, x, y;
var r = 10;
with (Math) {
a = PI * r * r;
x = r * cos(PI);
y = r * sin(PI / 2);
}
In the statement following the with
keyword, the default object is the Math
object, so PI
, cos
and sin
are properties of the Math
object without explicitly defining it.
Our question could then be translated like this
(function(x, undefined){}).length;
The length
property of a function returns the number of its arguments, so the answer to the question is 2.
Bonus Question (ES2015)
I decided to add a question (popped out on Twitter) written in ECMAScript2015 (or ES6), the new major release of ECMAScript, which I’m currently learning and using for new projects.
let x = (() => {
for (var i = 0; i < 5; i++) {
try {
return i;
} finally {
if (i != 3) continue;
}
}
})();
console.log( x ); // ?
I’m assuming that you’re already familiar with the new syntax and things like let
or the fat arrow function notation don’t scare you, otherwise this guide may be a valid starting point. However note that the behaviour of this function is equivalent in ES5.
The essential question is the following: is the function returning the first value of i
in the for loop, which would be 0, or returns 3?
The answer is the latter, the finally
block has the precedence on the entire try-catch-finally
production, as MDN points out
If the finally block returns a value, this value becomes the return value of the entire try-catch-finally production, regardless of any return statements in the try and catch blocks. This includes exceptions thrown inside of the catch block.