Original text: dev. The to/aman_singh /…

Translator: Front-end small wisdom

This does not have a dafang background, but has a positive attitude of people. In this paper, making github.com/qq449245884… Has been included, the article has been classified, also collated a lot of my documentation, and tutorial materials.

Everyone said there was no project to write on the resume, so I found a project for everyone, and I gave it as a bonus【 Building tutorial 】.

Now, let’s take a look at a couple of questions that you can think about and answer.

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: Does the UI of the page (TAB) still respond if you run the following function in the console

function foo() {
  return Promise.resolve().then(foo);
};    
Copy the code

Question 6: Can we somehow use expansion for the following statement without causing a type error

var obj = { x: 1, y: 2, z: 3 };
[...obj]; // TypeError 
Copy the code

Question 7: What is printed on the console when the following code snippet is run?

var obj = { a: 1, b: 2 };
Object.setPrototypeOf(obj, {c: 3});
Object.defineProperty(obj, 'd', { value: 4, enumerable: false });

// what properties will be printed when we run the for-in loop?
for(let prop in obj) {
    console.log(prop);
}  
Copy the code

Question 8: What value does xGetter() print?

var x = 10; var foo = { x: 90, getX: function() { return this.x; }}; foo.getX(); // prints 90 var xGetter = foo.getX; xGetter(); // prints ??Copy the code

The answer

Now, let’s answer each question from start to finish. I’ll give you a brief explanation, while trying to demyserize these behaviors and provide some references.

Question 1:undefined

Resolution:

Variables declared with 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, variables declared by var are function scoped, while let and const are block scoped. So, this is what the process looks like:

var a = 10; Function foo() {// var the declaration of a will be promoted to the top of the function. // For example :var a console.log(a); Var a = 20; var a = 20; // local scope }Copy the code

Question 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; Function foo() {// start with TDZ // create uninitialized 'a' console.log(a); // ReferenceError // TDZ end, 'a' is only initialized here, the value is 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)

Resolution:

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.

Var array = []; for (var i = 0; i < 3; I ++) {// Each ' 'I' 'in the body of the three arrow functions points to the same binding, // that's 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.

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

Resolution:

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

  1. Calling foo() places the function foo on the call stack.

  2. The JS engine encounters setTimeout while processing internal code.

  3. The foo callback function is then passed to the WebAPIs(arrow 1) and returned from the function, leaving the call stack empty again

  4. The timer is set to 0, so foo will be sent to the task queue (arrow 2).

  5. Since the call stack is empty, the event loop selects the foo callback and pushes it onto the call stack for processing.

  6. The process repeats again, and the stack does not overflow.

The operation diagram is as follows:


Problem 5: No response

Resolution:

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.


Problem 6: TypeError is caused

Resolution:

The iterable object defines the data to be traversed. An Array or Map is a built-in iterator with a default iteration behavior. Objects are not iterable, but they can be made iterable by using the iterable and iterator protocols.

In the Mozilla documentation, an object is iterable if it implements the @@iterator method, which means that the object (or an object in its prototype chain) must have an attribute with the @@iterator key, which can be obtained through the constant Symbol.

The above statement may seem a little verbose, but the following example makes more sense:

var obj = { x: 1, y: 2, z: 3 }; Obj [Symbol. Iterator] = function() {// iterator is an object with a next method, // it returns at least one object // two properties: value&done. Return {next: function() {if (this._countdown === 3) {const lastValue = this._countdown; return { value: this._countDown, done: true }; } this._countDown = this._countDown + 1; return { value: this._countDown, done: false }; }, _countDown: 0 }; }; [...obj]; // Print [1, 2, 3]Copy the code

You can also use generator functions to customize the iterative behavior of an object:

var obj = {x:1, y:2, z: 3} obj[Symbol.iterator] = function*() { yield 1; yield 2; yield 3; } [...obj]; // Print [1, 2, 3]Copy the code

Question 7. A, B, C

Resolution:

The for-in loop iterates over the enumerable properties of the object itself and the properties that the object inherits from its prototype. An enumerable property is a property that can be included and accessed during a for-in loop.

var obj = { a: 1, b: 2 };
var descriptor = Object.getOwnPropertyDescriptor(obj, "a");
console.log(descriptor.enumerable); // true
console.log(descriptor);
// { value: 1, writable: true, enumerable: true, configurable: true }
Copy the code

Now that you have this knowledge, it should be easy to understand why our code prints these specific properties

var obj = { a: 1, b: 2 }; // set {c: 3} to the stereotype of 'obj', and we know that // the for-in loop also iterates over the attribute inherited by obj // From its stereotype, 'c' can also be accessed. Object.setPrototypeOf(obj, { c: 3 }); // We define another property 'd' in 'obj', but we set 'Enumerable' to false. This means that 'd' will be ignored. Object.defineProperty(obj, "d", { value: 4, enumerable: false }); for (let prop in obj) { console.log(prop); } // Print // a // b // cCopy the code

Question 8:10

Resolution:

When x is initialized globally, it becomes a property of the Window object (not strictly). Look at the following code:

var x = 10; // global scope var foo = { x: 90, getX: function() { return this.x; }}; foo.getX(); // prints 90 let xGetter = foo.getX; xGetter(); // prints 10Copy the code

We can assert that:

window.x === 10; // true
Copy the code

This always points to the object that called the method. So, in the case of foo.getx(), it points to an object foo and returns a value of 90. In the case of xGetter(), this points to the window object and returns the value of x in window, which is 10.

To get the value of foo.x, 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(); / / 90Copy the code

In this way! If all your answers are correct, well done. We all learn by making mistakes. It’s all about understanding the “why” behind it.

The possible bugs after the deployment of code can not be known in real time, in order to solve these bugs, spent a lot of time log debugging, here by the way to recommend you a good BUG monitoring toolFundebug.

communication

The article is updated every week. You can search “Big Move World” on wechat for the first time to read and hurry up (one or two articles earlier than the blog yo). This article is GitHub github.com/qq449245884… Welcome to Star and improve. You can refer to the exam site for review during the interview. In addition, you can follow the public account and reply welfare in the background, so that you can see the welfare, you know.