The ultimate ES5 JavaScript quiz


So, you have just finished your supreme-js-learning-pack-for-idiots and coded some nice app and now are on the top of the world. You hail your code as supreme and think that now you have mastered everything. Then you are the right candidate for my quiz, which will test your abilities of dry running the program and knowing the idomatic ES5 (JavaScript) programming paradigms, along with some its usual quirks! :-) (Note: ES6 is not being tested here)

JavaScript - easy to learn, hard to master!

The quiz is subjective type. You are given a program snippet and are supposed to figure out the output. The program may have an (intentionally inserted) error, so beware while deciding your answer. Assume that all snippets execute in a browser environment (in the global scope) with "use strict;" mode enabled. Each output has been tested on Chrome mainstream build and made browser independent. If you still feel your browser's console output is different, let me know in the comments.

Questions:


Question 1
1:  var x = y = z = 10;  
2:    
3:  console.log(typeof y);  
An error would be thrown, because the "use strict;" mode forbids declarations without a `var` keyword. Currently, our code has two such declarations - `y` and `z`. Even though they appear to be a part of a var declaration starting with `x`, they are actually created as variables without a `var`, for
they are interpreted as:

1:  var x = (y = (z = 10));  

The interpreter sees a `z = 10` declaration, and tries to create `z` since it doesn't already exist. And since the declaration has no `var` attached to it, an error is thrown. The correct code would be:

1:  // use commas  
2:  var x = 10, y = 10, z = 10;  
3:  // OR use two separate statements - one each for declaration and initialization  
4:  var x, y, z; x = y = z = 10;
Question 2
1:  for(var i = 1; i <= 5; i++){  
2:    setTimeout(function(){  
3:      console.log(i);  
4:    }, 500);  
5:  }  
The output would be:

 6  
 6  
 6  
 6  
 6  

Puzzled, right? Well, the simple reason for this is that `setTimeout` execution has not yet started when the loop finishes, making `i = 6` (> 5). "So, what? I had logged the current value of `i` as I was inside the loop!" This is exactly what puzzles many coders. Well, the fact is that the anonymous function passed as an argument to setTimeout maintains a closure over its outer scope (which is global). So, when it starts execution, it utilizes the current value of `i` present in the scope at the time it was executing. And since the loop had finished execution, that `i` value is `6`. To log 12345 in sequence we would have to deliberately cause the function to maintain a closure over the required `i` value. So, we can do:

1: for(var i = 1; i <= 5; i++){    
2:    setTimeout((function outer(i){   
3:     return function returned(){   
4:      console.log(i);   
5:     }   
6:    })(i), 500);   
7: }   

The `outer` function is an immediately invoked function expression. During loop execution, the first time, it is passed the `i` value of `1` (line 6) as an argument. Now, since `returned` function is inside the scope of `outer` function and `outer` has a `i = 1`, so, when `returned` executes, it logs `1` (for that it is the value to which it has access). Similar is the case with the second, third, fourth and fifth loop execution. Try rereading this if you can't understand it for the first time.

Question 3
1:  var obj = {  
2:    prop: "42",  
3:    getIt: function(){  
4:      console.log("searching");  
5:      setTimeout(function getter(){  
6:        console.log(this.prop);  
7:      }, 500);  
8:    }  
9:  };  
10:    
11:  obj.getIt();  
The output would be:

1:  searching  
2:  undefined  

This output is generated because the function `getter` is executed inside of the setTimeout function, so it has lost its `this` reference, and therefore is not able to resolve `this.prop`, logging `undefined`. You can easily `console.log(this)` inside of `getter` to see that it points to `window`.
Question 4
1:  var arr = new Array(10);  
2:  console.log(arr.map(function(){return 0;}));  
The output would be:

[] 

We need to note that the array `arr` with `new Array` method, which creates an array of specified length, but never assigns a value to the any of the indices. And, `.map` always skips over the indices which haven't been assigned a value. Thus, an empty array is returned. To get an array with 10 elements, all being 0, we would do:
1:  Array.apply(null, new Array(5)).map(function() { return 0; });  
Question 5
1:  console.log("abcdefgh".substr(3, 5));  
The output would be:

defgh

If you have read the spec carefully, you would know that `.substr` receives an index and the number of characters to slice beginning from the index. The second argument is not the index before which slicing is to end.
Question 6
1:  var arr = [1, 2, 3, 4, 5];   
2:  arr.push(arr.unshift(5));   
3:    
4:  console.log(arr);   
The output is:
[ 5, 1, 2, 3, 4, 5, 6 ]  
Because both `.unshift` and `.push` return the number of elements left in the array after performing the operation.
Question 7
1:  console.log(JSON.parse(JSON.stringify(window)));
`window` object (and all other `Node` objects) are special in a sense that they carry a reference to themselves through their properties. This is called a circular reference structure, and JSON is pretty poor at handling this. Thus, it raises a TypeError the moment `JSON.stringify` is called on `window`.
Question 8
1:  var x = Object.create({}, {  
2:    "prop1": {  
3:      value: 5,  
4:      enumerable: true  
5:    },  
6:    "prop2": {  
7:      value: 6,  
8:      enumerable: true  
9:    }  
10:  });  
11:    
12:  x.prop2 = 7;  
13:    
14:  console.log(x.prop3);  
Since both properties are not writable, therefore, `x.prop2 = 7` doesn't change `prop2`'s value. However, it doesn't also raise any error, as you might expect. Thus, the output is `undefined` (since "prop3" property is not defined for `x`)
Question 9
Just for this one, execute each line as if in a separate program
1:  console.log(13..toString().replace("1", ""));
2:  console.log(1e3..toString().replace("e", ""));
For line 1, the output is "3". The ".." doesn't give a syntax error. This is because, on encountering the first ".", the interpreter expects 13 to be a decimal number with some decimal digits after it. However, almost immediately, it gets another "." which it, therefore, sees as a property access notation. Then, it executes `.toString().replac` call to obviously give "3" as the output. For line 2, the output it an error! Surprised? Well, this is because the "1e3" notation is an exponential notation for a number, which gives 1 * 10^3 as the number. So, what's the problem, you ask. The problem is that it is already considered as a decimal number. So, the interpreter sees the first dot as property access, and raises an error for the unexpected second dot.
Question 10
!function(c,f){c[f]("Quiz finished!");}(window,"alert");
If you haven't seen it already,
!function(){}();
is the usage for an Immediately Invoked Function Expression. `c` argument refers to the `window` object and `f` is the string "alert". So, `c[f]` is equivalent to `window["alert"]` method call which thus produces an alert "Quiz finished!"
So, that's it guys! A small, 10 question quiz that must have been an easy success for you if you had learnt how to dry run any JS code well, and not just memorized it! How much did you score? I would love to hear your experience in the comments! :-)

About Gaurang Tandon
Gaurang is an avid lover of latest technology, expecially interested in JS. Being a student, he does self-training of ES5 and ES6 at home, and is perpetually up-to-date with the web dev community. He has also made a Chrome extension, ProKeys, which already has 600+ users, along with many fun side-projects. He is also interested in advanced maths and chess.

Comments

Popular posts from this blog

Anatomy of a game

Overclock your brain power - Brain Hacks