JavaScript Quiz and Explanations of its Answers

Update: Comments are now enabled.

In the field of linguistics, rules can be either descriptive or prescriptive. Simply put, descriptive rules tell you what can be done and prescriptive rules tell you what you shouldn't do. Many articles about JavaScript give you prescriptions: they tell you what you shouldn't do in JavaScript because it's bad practice. After learning to stay away from certain styles and patterns, though, sometimes a simple typo or brain fart can avalanche into a disaster in your code and still leave you puzzled even after attempting, descriptively, to explain what exactly happened.

kangax's JavaScript quiz, geared towards web developers, helps exactly that: finding descriptions that clarify perplexing situations. It does not test your knowledge of any DOM behavior and is focused on the ECMAScript language itself. Since this quiz targets web developers, SpiderMonkey gurus can likely even point to where a given quirk (the term is being used very loosely here) is manifested in the implementation. As always, please point out any possible error in the answer explanations about to follow. Those who are interested but still haven't taken it: halt! Go and take it. I'll wait. After that, read on.

You're done? Then here beginneth an obligatorie Spoiler Alerte and the Solutions to aforementioned quiz Questions.

Question 1

(function(){
  return typeof arguments;
})();

The answer is "object"; the arguments object accessible in a function is, well, an object. There are two things to watch out for here:

  • arguments is not an array. It is an array-like object. You can thus access its elements with square brackets and integer indices, but methods usually available on an array such as push do not exist on arguments.
  • Even if arguments were a true array, the answer would still be "object". The typeof operator returns "object" when given an array, because arrays are objects too.

Question 2

var f = function g(){ return 23; };
typeof g();

The answer is that an error will occur. This is because function g(){ return 23; } is a function expression (a named one, in fact), not a function declaration. The function is actually bound to the variable f, not g. Specifying an identifier in a function expression does have its use though: stack traces are clearer instead of being littered with nameless functions, and you can have an anonymous function recursively call itself without using arguments.callee. kangax has a very detailed post on function expressions.

Question 3

(function(x){
  delete x;
  return x;
})(1);

This gives 1. The quickest way to put it is that x is regarded as being created with a variable declaration, not with a property assignment. Thus x is marked with the DontDelete flag and delete x would fail by yielding false (but not by throwing an error). For an in-depth explanation regarding the semantics and behavior of the delete operator, kangax wrote an article titled "Understanding delete".

Question 4

var y = 1, x = y = typeof x;
x;

x would become the string "undefined". It is easier to understand why by rewriting the code with the following observations:

  • var a, b; expands to var a; var b;.
  • a = b = c; is equivalent to b = c; a = b;

Knowing this, we rewrite and get:

var y = 1;
y = typeof x;
var x = y;
x;

When y = typeof x is executed, x has not been defined yet, so y becomes the string "undefined", which is then finally assigned to x when it is formally declared.

Question 5

(function f(f){
  return typeof f();
})(function(){ return 1; });

Running the above yields the string "number". To illustrate more clearly, the above can be rewritten as thus:

var baz = function(){ return 1; };
(function f(f){
  return typeof f();
})(baz);

Here, function f takes one argument, another function, calls said function, and returns the type of whatever is returned from calling said function. Even though the argument name f clashes with the function name f, the argument f effectively shadows the variable f: what previously refers to the function itself now refers to the function's first argument.

Question 6

var foo = { 
  bar: function() { return this.baz; }, 
  baz: 1
};
(function(){ 
  return typeof arguments[0]();
})(foo.bar);

"undefined" is returned. To understand why, one must know how the this operator works. As stated on MDC, this refers to the current context object, so this refers to different things depending on how a function is called. Here we're performing a plain old method call in arguments[0](). Because we're calling foo.bar with the name arguments[0], this in foo.bar is not bound to foo, but bound to whatever this refers to within the anonymous function! Here, that happens to be the global object. There is no property called baz in the global object, so typeof operator yields "undefined".

Question 7

var foo = {
  bar: function(){ return this.baz; },
  baz: 1
}
typeof (f = foo.bar)();

The answer is also "undefined". This question is a variant of question 6, since it can rewritten as thus:

var foo = {
  bar: function(){ return this.baz; },
  baz: 1
}
f = foo.bar;
typeof f();

foo.bar is stored into f and then called, so the this in foo.bar refers to the global object, which does not contain the property baz. Therefore typeof f() yields the string "undefined".

Question 8

var f = (function f(){ return "1"; }, function g(){ return 2; })();
typeof f;

typeof f yields "number". The use of the comma operator here can be very confounding, but this snippet illustrates its behavior:

var x = (1, 2, 3);
x;

x evaluates to 3. This reveals that when you have a series of expressions grouped together and separated by commas, they are evaluated from left to right, but only the last expression's result is preserved. By the same token, the question can be rewritten to be less confusing:

var f = (function g(){ return 2; })();
typeof f;

By substituting one more time, typeof f is basically typeof 2.

Question 9

var x = 1;
if (function f(){}) {
  x += typeof f;
}
x;

x becomes the string "1undefined". Here, the dilemma is: is function f(){} a function expression or function declaration? (It is syntactically valid in both cases.) Since the if statement takes an expression, function f(){} is interpreted as one, so the code might as well been:

var x = 1;
if (function(){}) {
  x += typeof f;
}
x;

Functions are always truthy values in JavaScript, so after inlining the if conditional we obtain:

var x = 1;
x += typeof f;
x;

f is not defined here, so typeof f yields the string "undefined". Adding a string to a number results in a string; we end up with "1undefined" as the value of x.

Question 10

var x = [typeof x, typeof y][1];
typeof typeof x;

typeof typeof x is "string". The quickest logic to this is to observe that typeof x always yields a string, no matter what x is. typeof typeof x thus always results in the string "string". As a side note, [typeof x, typeof y][1] can be simplified to typeof y after evaluation.

Question 11

(function(foo){
  return typeof foo.bar;
})({ foo: { bar: 1 } });

The above expression results in the string "undefined". The confusion factor is easier to eliminate by rewriting multiple times. First, we pull out the anonymous function's only argument:

var baz = { foo: { bar: 1 } };
(function(foo){
  return typeof foo.bar;
})(baz);

Next, we eliminate the function by inlining it:

var baz = { foo: { bar: 1 } };
var foo = baz;
typeof foo.bar;

Finally, by substitution we remove the intermediate variable foo as well:

var baz = { foo: { bar: 1 } };
typeof baz.bar;

At this point it becomes clear that the property bar is not defined for baz; it is defined for baz.foo. typeof baz.bar therefore yields "undefined".

Question 12

(function f(){
  function f(){ return 1; }
  return f();
  function f(){ return 2; }
})();

This snippets results in the value 2. As Ben Cherry pointed out, this requires knowing how function declaration hoisting works. First we can observe that the following code works correctly and yields the value 5:

f();
function f() { return 5; }

Even when the declaration of the function f occurs after the call to f, the snippet works correctly. This is because function declarations are first hoisted to the top of the scope and then evaluated in order. Because of this, the previous example is equivalent to:

function f() { return 5; }
f();

After knowing this, we can then understand that after hoisting, the code in question is equivalent to:

(function f(){
  function f(){ return 1; }
  function f(){ return 2; }
  return f();
})();

Originally, f refers to the top-level anonymous function itself. It, however, no longer does, after the declaration function f(){ return 1; }. Compounding to that, the second declaration function f(){ return 2; } overwrites what is done in the first declaration. At this point, f refers to a function that returns the value 2. We then call this function and then return the result it returned, namely, 2.

Question 13

function f(){ return f; }
new f() instanceof f;

new f() instanceof f yields false. To understand why, it must first be known what new f() yields. The new operator creates a new object and calls the constructor function with this new object as its current context object. In other words, within the constructor, this points to the new object that is currently being created. After calling the constructor, the default semantics of the new operator is to yield said new object, even if the constructor returns some value. As Andy Tijn points out, this behavior has one exception: when the constructor returns an object, the new operator will yield the returned object. To illustrate this, consider the following code:

var z = {a: 2};
function g() { return z; }
var x = new g();
x === z;

Here, x is actually equal to z, down to the identity! With this in mind, we know that new f() === f. The question now is what f instanceof f would yield. We know that f itself is a function, so it is an instance of the Function object. The fact that f instanceof Function === true proves that f instanceof f yields false.

Question 14

with (function(x, undefined){}) length;

The answer is 2. The above can be rewritten as:

(function(x, undefined){}).length;

Each function has a length property that indicates its arity, i.e. the number of arguments it takes.

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.