JS has a few concepts where people tend to take it lightly and sometime may tumble over. Concepts like prototyping, closures and event-loops are still one of those obscure areas where most of the JS developers take a detour. And as we know “little knowledge is a dangerous thing”, it may lead to making mistakes.
Let’s play a mini-game where I am going to ask you a few questions and you have to try answering all of them. Make a guess even if you don’t know the answer or if it’s out of your knowledge. Note down your answers and then check the corresponding answers below. Give yourself a score of 1 for each correct answer. Here we go.
Disclaimer: I’ve used ‘var’ purposely in some of the following code snippets so that you guys can copy paste them into your browser’s console without running into SyntaxError problem. But I don’t condone the usage of ‘var’ declared variables. ‘let’ and ‘const’ declared variables make your code robust and less error-prone.
Question 1: What will be printed on the browser console?
Question 2: Will output be the same if we use let or const instead of var?
Question 3: What elements will be in the “newArray”?
Question 4: If we run the ‘foo’ function in the browser console, will it cause a stack overflow error?
Question 5: Will the UI of the page (tab) remains responsive if we run the following function in the console?
Question 6: Can we somehow use the spread syntax for the following statement without causing a TypeError?
Question 7: What will be printed on the console when we run the following code snippet?
Question 8: What value xGetter() will print?
Now, let’s try to answer each question from top to bottom. I will give you a brief explanation while trying to demystify these behaviors along with some references.
Answer 1: undefined
Answer 2: ReferenceError: a is not defined
Answer 3: [3, 3, 3]
Explanation: Declaring a variable with var keyword in the head of for loop creates a single binding (storage space) for that variable. Read more about closures. Let’s look at the for loop one more time.
If you let-declare a variable, which has a block-level scope, a new binding is created for each loop iteration.
Another way of solving this quirk would be to use closures.
Answer 4: No.
JS call stack is Last In First Out (LIFO). The engine takes one function at a time from the stack and runs the code sequentially from top to bottom. Every time it encounters some asynchronous code, like setTimeout, it hands it over to the Web API (arrow 1). So, whenever an event is triggered, the callback gets sent to the Task Queue (arrow 2).
The Event loop is constantly monitoring the Task Queue and process one callback at a time in the order they were queued. Whenever the call stack is empty, the loop picks up the callback and put it into the stack (arrow 3) for processing. Keep in mind that if call stack is not empty, event loop won’t push any callbacks to the stack.
Now, armed with this knowledge, let’s try to answer the aforementioned question.
Answer 5: No.
Explanation: Most of the time, I have seen developers assuming that we have only one task queue in the event loop picture. But that’s not true. We can have multiple task queues. It’s up to the browser to pick up any queue and processes the callbacks inside.
Microtask queue is always emptied before execution returns to the event loop
Now, when you run the following code snippet in your console:
Every single invocation of ‘foo’ will continue to add another ‘foo’ callback on the microtask queue and thus the event loop can’t continue to process your other events (scroll, click etc) until that queue has completely emptied. Consequently, it blocks the rendering.
Answer 6: Yes, by making object iterables.
Explanation: The spread syntax and for-of statement iterates over data that iterable object defines to be iterated over. Array or Map are built-in iterables with default iteration behaviour. Objects are not iterables, but you can make them iterable by using iterable and iterator protocols.
In Mozilla documentation, an object is said to be iterable if it implements the @@iterator method, meaning that the object (or one of the objects up its prototype chain) must have a property with a @@iterator key which is available via constant Symbol.iterator.
The aforesaid statement may seem a bit verbose, but the following example will make more sense:
You can also use a generator function to customize iteration behaviour for the object:
Answer 7: a, b, c
Explanation: The for-in loop iterates over the enumerable properties of an object itself and those the object inherits from its prototype. An enumerable property is one that can be included in and visited during for-in loops.
Now having this knowledge in your bag, it should be easy to understand why our code printed those specific properties.
Answer 8: 10
Explanation: When we initialised x into the global scope, it becomes the property of the window object (assuming that it’s a browser environment and not a strict mode). Looking at the code below :
We can assert that:
window.x === 10; // true
this will always point to the object onto which the method was invoked. So, in the case of foo.getX(), this points to foo object returning us the value of 90. Whereas in the case of xGetter(), this points to the window object returning us the value of 10.
To retrieve the value of foo.x, we can create a new function by binding the value of this to the foo object using Function.prototype.bind.
let getFooX = foo.getX.bind(foo);getFooX(); // prints 90
That’s all! Well done if you’ve got all of your answers correct. We all learn by making mistakes. It’s all about knowing the ‘why’ behind it. Know your tools and know them better.
Please don’t forget to leave a 👏 if you like the article. What was your score anyway 😃?