So do you think you know JavaScript?

前端小智
中文
Author: Chidume Nnamdi
Translator: Frontend Xiaozhi
Source: smashingmagazine

Like and look again, develop a habit

This article GitHub has been included on https://github.com/qq449245884/xiaozhi . There are more categories of highly praised articles in the past, and a lot of my documents and tutorial materials have been sorted out. Welcome to Star and perfect. You can refer to the test center for review during the interview. I hope we can have something together.

JavaScript is an interesting language, and we all like it because of its nature. The browser is the main place where JavaScript runs, and the two work together in our service. JS has some concepts, people tend to take it lightly, and sometimes they may ignore it. Concepts such as prototypes, closures, and event loops are still one of the obscure areas where most JS developers take a detour. As we know, ignorance is a dangerous thing, and it can lead to errors.

you want to read more high-quality articles, please [Poke GitHub Blog][1]\, hundreds of high-quality articles a year are waiting for you!

Next, let's take a look at a few questions, you can also try to think about it, and then answer.

Question 1: What will be printed on the browser console?

var a = 10;
function foo() {
    console.log(a); // ??
    var a = 20;
}
foo();

Question 2: If we use let or const instead of var, will the output be the same

var a = 10;
function foo() {
    console.log(a); // ??
    let a = 20;
}
foo();

Question 3: What are the elements in "newArray"?

var array = [];
for(var i = 0; i <3; i++) {
 array.push(() => i);
}
var newArray = array.map(el => el());
console.log(newArray); // ??

Question 4: If we run the'foo' function in the browser console, will it cause a stack overflow error?

function foo() {
  setTimeout(foo, 0); // 是否存在堆栈溢出错误?
};

Question 5: If you run the following function in the console, does the UI of the page (tab) still respond?

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

Question 6: Can we use the expansion operation for the following statement in some way without causing a type error

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

Question 7: When I run the following code snippet, what will be printed on the console?

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);
}

Question 8: What value will xGetter() print?

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

answer

Now, let us answer each question from beginning to end. I will give you a brief explanation, try to uncover the mystery of these behaviors, and provide some reference materials.

Question 1: undefined

Analysis:

Variables declared using the var keyword will be promoted in JavaScript, and the value undefined will be allocated in memory. But initialization happens exactly where you assign a value to the variable. In addition, var are [function-scoped][2], while let and const are block-scoped. So, this is how this process looks like:

var a = 10; // 全局使用域
function foo() {
// var a 的声明将被提升到到函数的顶部。
// 比如:var a

console.log(a); // 打印 undefined

// 实际初始化值20只发生在这里
   var a = 20; // local scope
}
    • -

Question 2: ReferenceError:a undefined .

Analysis:

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) . Attempting to access these variables in TDZ ReferenceError because they can only be accessed when the execution reaches the declaration.

var a = 10; // 全局使用域
function foo() { // TDZ 开始

// 创建了未初始化的'a'
    console.log(a); // ReferenceError

// TDZ结束,'a'仅在此处初始化,值为20
    let a = 20;
}

The following table summarizes the promotion behavior and usage domains corresponding to variables declared by different keywords used in JavaScript:

clipboard.png

    • -

Question 3: [3, 3, 3]

Analysis:

In for head statement cycle with var variable keyword that will create a single variable bindings (storage space). Read more about [closure][3]. Let's look at the for loop again.

// 误解作用域:认为存在块级作用域
var array = [];
for (var i = 0; i < 3; i++) {
  // 三个箭头函数体中的每个`'i'`都指向相同的绑定,
  // 这就是为什么它们在循环结束时返回相同的值'3'。
  array.push(() => i);
}
var newArray = array.map(el => el());
console.log(newArray); // [3, 3, 3]

If you use let declare a variable with block-level scope, a new binding is created for each loop iteration.

// 使用ES6块级作用域
var array = [];
for (let i = 0; i < 3; i++) {
  // 这一次,每个'i'指的是一个新的的绑定,并保留当前的值。
 // 因此,每个箭头函数返回一个不同的值。
  array.push(() => i);
}
var newArray = array.map(el => el());
console.log(newArray); // [0, 1, 2]

Another way to solve this problem is to use [closure][4].

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]
    • -

Question 4: Will not overflow

Analysis:

The JavaScript concurrency model is based on the "event loop". When we say "the browser is the home of JS" I really mean that the browser provides a runtime environment to execute our JS code.

The main components of the browser include call stack , event loop **, task queue and Web API**. Global functions like setTimeout , setInterval and Promise are not part of JavaScript, but part of Web API. The visual form of the JavaScript environment is as follows:

clipboard.png

The JS call stack is last in first out (LIFO). The engine removes a function from the stack at a time, and then runs the code sequentially from top to bottom. Whenever it encounters some asynchronous code, such as setTimeout , it Web API it to 06078da915e6f0 (arrow 1). Therefore, each time an event is triggered, callback will be sent to the task queue (arrow 2).

Event loop constantly monitors the Task Queue and processes one callback at a time in the order in which they are queued. Whenever the call stack is empty, the Event loop gets the callback and puts it into the stack (stack) (arrow 3) for processing. Remember, if the call stack is not empty, will not push any callbacks onto the stack .

Now, with this knowledge, let us answer the questions mentioned earlier:

step

  1. Calling foo() will put the foo function into the call stack .
  2. When dealing with internal code, the JS engine encountered setTimeout .
  3. Then pass the foo callback function to WebAPIs (arrow 1) and return from the function, the call stack is empty again
  4. The timer is set to 0, so foo will be sent to

task queue
(Arrow 2).

  1. Since the call stack is empty, the event loop will select the foo callback and push it into the call stack for processing.
  2. The process repeats again and the stack will not overflow.

The operation diagram is as follows:

\![Picture description][5]

    • -

Question 5: Will not respond

Analysis:

Most of the time, developers assume that
is only one task queue in the 16078da915e893 event loop But this is not the case, we can have multiple task queues. Wherein a queue is selected by the browser and the queue handling callbacks



At the bottom level, there are macro tasks and micro tasks in JavaScript. setTimeout callback is the macrotask , and the Promise callback is the .

The main difference lies in the way they are implemented. Macro tasks are pushed onto the stack one at a time in a single cycle, but the micro task queue is always emptied after execution before returning to the event loop. Therefore, if you add items to this queue at the speed of processing items, you will always be processing microtasks. Only when the microtask queue is empty, the event loop will re-render the page,

Now, when you run the following code snippet in the console

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

Each call to ' foo ' will continue to add another ' foo ' callback to the microtask queue, so the event loop cannot continue to process other events (scrolling, clicking, etc.) until the queue is completely emptied. Therefore, it will prevent rendering.

    • -

Question 6: Will cause TypeError

Analysis:

[Expand Syntax][6] and [for-of][7] The statement traverses the iterable object to define the data to be traversed. Array or Map are built-in iterators with default iteration behavior. Objects are not iterable, but they can be made iterable by using the [iterable][8] and [iterator][9] protocols.

In the Mozilla document, if an object implements the @@iterator method, then it is iterable, which means that the object (or an object on its prototype chain) must have a property with the key @@iterator Symbol.iterator by the constant 06078da915e9ba.

The above statement may seem a bit verbose, but the following example will make more sense:

var obj = { x: 1, y: 2, z: 3 };
obj[Symbol.iterator] = function() {

  // iterator 是一个具有 next 方法的对象,
  // 它的返回至少有一个对象
  // 两个属性:value&done。

  // 返回一个 iterator 对象
  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]; // 打印 [1, 2, 3]

You can also use the [generator][10] function to customize the iteration behavior of the object:

var obj = {x:1, y:2, z: 3}
obj[Symbol.iterator] = function*() {
  yield 1;
  yield 2;
  yield 3;
}
[...obj]; // 打印 [1, 2, 3]
    • -

Question 7: a, b, c

Analysis:

for-in loops through the [enumerable properties][11] of the object itself and the properties inherited by the object from its prototype. Enumerable attributes are attributes that can be included and accessed during the cycle of for-in

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 }

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

var obj = { a: 1, b: 2 }; //a,b 都是 enumerables 属性

// 将{c:3}设置为'obj'的原型,并且我们知道
// for-in 循环也迭代 obj 继承的属性
// 从它的原型,'c'也可以被访问。
Object.setPrototypeOf(obj, { c: 3 });

// 我们在'obj'中定义了另外一个属性'd',但是 
// 将'enumerable'设置为false。 这意味着'd'将被忽略。
Object.defineProperty(obj, "d", { value: 4, enumerable: false });

for (let prop in obj) {
  console.log(prop);
}
// 打印
// a
// b
// c
    • -

\#\#\#\# Question 8: 10

Analysis:

x in the global scope, it becomes a property of the window object (not in strict mode). Take a 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 10

We can assert:

window.x === 10; // true

this always points to the object calling the method. Therefore, in foo.getx() , it points to the foo object and returns the value of 90 In xGetter() case, the this point window object, returns window in x value, i.e. 10 .

To get the value of foo.x , you can create a new function Function.prototype.bind to this the value of foo

let getFooX = foo.getX.bind(foo);
getFooX(); // 90

that's all! If all of your answers are correct, then do pretty. We all learn by making mistakes. All this is to understand the "reason" behind it.

    • -

may have bugs that may exist after code deployment cannot be known in real time. In order to solve these bugs afterwards, a lot of time was spent on log debugging. By the way, I would like to recommend a useful BUG monitoring tool Fundebug

Original: https://dev.to/aman_singh/so-you-think-you-know-javascript-5c26

communicate with

The dry goods series articles are summarized as follows. I think it’s good to order a Star. Welcome to join the group to learn from each other.

https://github.com/qq449245884/xiaozhi

I am Xiaozhi, the author of the I am a fan of learning front-end technology. I will often share what I have learned and read , and encourage each other on the way to advancement!

attention to the public account, and reply to 16078da915ec67 welfare background, you can see the welfare, you know.

clipboard.png

[1]: https://www.fundebug.com/?utm\_source=xiaozhi

阅读 634

终身学习者
我要先坚持分享20年,大家来一起见证吧。
avatar
前端小智
前端开发工程师

我不是什么大牛,我其实想做的就是一个传播者。内容可能过于基础,但对于刚入门的人来说或许是一个窗口,一个解惑之窗。我要先坚持分享20年,大家来一起见证吧。

59.1k 声望
74.2k 粉丝
0 条评论
你知道吗?

avatar
前端小智
前端开发工程师

我不是什么大牛,我其实想做的就是一个传播者。内容可能过于基础,但对于刚入门的人来说或许是一个窗口,一个解惑之窗。我要先坚持分享20年,大家来一起见证吧。

59.1k 声望
74.2k 粉丝
宣传栏