This is the seventh day of my participation in the More text Challenge. For details, see more text Challenge
Question 1: What will be printed on the browser console?
var a = 10;
function foo() {
console.log(a); // ??
var a = 20;
}
foo();
Copy the code
Question 2: If we use let or const instead of var, is the output the same?
var a = 10;
function foo() {
console.log(a); // ??
let a = 20;
}
foo();
Copy the code
Question 3: What elements are in “newArray”?
var array = [];
for(var i = 0; i <3; i++) {
array.push(() = > i);
}
var newArray = array.map(el= > el());
console.log(newArray); // ??
Copy the code
Question 4: If we run the ‘foo’ function in the browser console, does it cause a stack overflow error?
function foo() {
setTimeout(foo, 0); // Is there a stack overflow error?
};
Copy the code
Question 5: If I run the following function in the console, does the UI of the page (TAB) still respond?
function foo() {
return Promise.resolve().then(foo);
};
Copy the code
The answer:
Problem 1: undefined
Variables declared using the var keyword are promoted in JavaScript and the value undefined is allocated in memory. But initialization happens exactly where you assign a value to the variable. In addition, var declares variables that are [function scoped][2], while let and const are block-scoped. So, this is what the process looks like:
var a = 10; // Global domain
function foo() {
// var a declaration will be promoted to the top of the function.
// For example :var a
console.log(a); / / print undefined
// The actual initialization value of 20 only happens here
var a = 20; // local scope
}
Copy the code
Problem 2: ReferenceError: a undefined.
Resolution:
The let and const declarations allow a variable to be limited in its scope by the block, statement, or expression it uses. Unlike VAR, these variables are not promoted, and there is a so-called temporary dead zone (TDZ). Attempts to access these variables in TDZ raise a ReferenceError because they are only accessible when the arrival declaration is executed.
var a = 10; // Global domain
function foo() { / / start TDZ
// Create uninitialized 'a'
console.log(a); // ReferenceError
// end of TDZ, 'a' is only initialized here, with a value of 20
let a = 20;
}
Copy the code
The following table Outlines the promotion behavior and usage fields corresponding to variables declared with different keywords used in JavaScript:
Question 3: [3, 3, 3]
Declaring a variable with the var keyword in the header of a for loop creates a single binding (storage space) for that variable. Read more about closures. Let’s look at the for loop again.
// Misunderstand scope: assume that block-level scope exists
var array = [];
for (var i = 0; i < 3; i++) {
// Each 'I' in the body of the three arrow functions points to the same binding,
// This is why they return the same value '3' at the end of the loop.
array.push(() = > i);
}
var newArray = array.map(el= > el());
console.log(newArray); / / [3, 3, 3]
Copy the code
If you use let to declare a variable with block-level scope, a new binding is created for each loop iteration.
// Use ES6 block-level scope
var array = [];
for (let i = 0; i < 3; i++) {
// This time, each 'I' refers to a new binding and retains the current value.
Therefore, each arrow function returns a different value.
array.push(() = > i);
}
var newArray = array.map(el= > el());
console.log(newArray); / / [0, 1, 2]
Copy the code
Another way to solve this problem is to use closures
let array = [];
for (var i = 0; i < 3; i++) {
array[i] = (function(x) {
return function() {
return x;
};
})(i);
}
const newArray = array.map(el= > el());
console.log(newArray); / / [0, 1, 2]
Copy the code
Problem 4: No overflow
The JavaScript concurrency model is based on “event loops”. When we say “the browser is the home of JS” what I really mean is that the browser provides the runtime environment to execute our JS code.
The main components of the browser include the call stack, event loop, task queue, and Web API. Global functions such as setTimeout, setInterval, and Promise are not part of JavaScript, but part of Web API. A visual version of the JavaScript environment looks like this:The JS call stack is last in first out (LIFO). The engine pulls a function from the stack one at a time and runs the code from top to bottom. Whenever it encounters some asynchronous code, such as setTimeout, it hands it over to the Web API(arrow 1). Thus, the callback is sent to the task queue (arrow 2) whenever the event is fired.
The Event loop constantly monitors the Task Queue and processes callbacks one at a time in the order they are queued. Whenever the call stack is empty, the Event Loop gets the callback and puts it on the stack (arrow 3) for processing. Remember that if the call stack is not empty, the event loop does not push any callbacks onto the stack.
Now, armed with this knowledge, let’s answer the previous question:
Steps:
- Calling foo() places the function foo on the call stack.
- The JS engine encounters setTimeout while processing internal code.
- The foo callback function is then passed to the WebAPIs(arrow 1) and returned from the function, leaving the call stack empty again
- The timer is set to 0, so foo will be sent to the task queue (arrow 2).
- Since the call stack is empty, the event loop selects the foo callback and pushes it onto the call stack for processing.
- The process repeats again, and the stack does not overflow.
Problem 5: No response
Most of the time, developers assume that there is only one task queue in the event loop diagram. But that’s not the case. We can have multiple task queues. The browser selects one of these queues and processes the callback in that queue
At the bottom level, there are macro tasks and microtasks in JavaScript. The setTimeout callback is a macro task, while the Promise callback is a microtask.
The main difference is in how they are executed. Macro tasks are pushed onto the stack one at a time during a single cycle, but the microtask queue is always cleared after execution before returning to the event loop. So, if you’re adding items to this queue at the rate you’re processing items, you’re always processing microtasks. The event loop will rerender the page only if the microtask queue is empty,
Now, when you run the following code snippet in the console
function foo() {
return Promise.resolve().then(foo);
};
Copy the code
Each call to ‘foo’ continues to add another ‘foo’ callback to the microtask queue, so the event loop cannot continue processing other events (scrolling, clicking, etc.) until the queue is completely empty. Therefore, it will prevent rendering.