This section includes:
JS basic 1, 2, ES6, ADVANCED JS, asynchronous programming and so on, these knowledge is I read in a book, the name of the book I forget, not my own original. I was just tidying it up. Not that I didn’t want to add the title, I really forgot…


JS Basic knowledge and often test interview questions (1)

JS is a necessary skill for every front-end development, and we will also have several chapters to describe this knowledge in the booklet. First of all, we will familiarize ourselves with some basic knowledge points of JS which are often tested and easily confused.

Primitive

What kinds of primitive types are there? Is null an object?

In JS, there are six original values, which are:

  • boolean
  • null
  • undefined
  • number
  • string
  • symbol

First of all, primitive types store values and have no functions to call, such as undefined.toString().

At this point you must be wondering, this is not right, Ming Ming'1'.toString()It can be used. In fact, in this case,'1' It’s not a primitive type anymore, it’s cast to a String which is an object type, so it can be calledtoString Function.

In addition to strong-turning the type if necessary, primitive types have some pitfalls.

The number type of JS is floating point type, and it is used to meet some bugs, such as 0.1 + 0.2! == 0.3, but this will be covered in the advanced section. The string type is immutable, and no matter what method you call on the string, the value does not change.

In addition, for null, many people think it is an object type, which is not true. Although Typeof null does print object, this is a long-standing Bug in JS. In the original version of JS, the 32-bit system was used, and for performance considerations, the type information of variables was stored in low order. The beginning of 000 represents an object, whereas null represents all zeros, so it was wrongly identified as object. Although the current internal type determination code has changed, this Bug has persisted.

Object type

Scope question: What is the difference between object type and original type? What happens when function arguments are objects?

In JS, all objects except primitive types are object types. Unlike primitive types, which store values, object types store addresses (Pointers). When you create an object type, the computer will create a space in memory for the value, but we need to find the space, and the space will have an address (pointer).

const a = []Copy the code

For constant A, assuming the memory address (pointer) is #001, the value [] is stored at address #001, and the constant A is stored at address (pointer) #001

const a = []
const b = a
b.push(1)Copy the code

When we will be variable assigned to another variable, replication is originally the address of the variable (pointer), that is to say the current variable b store address (pointer) is # 001, when we modify the data will change in address (pointer) value in the history of # 001, has led to the two variables are changed.

Let’s look at the case where function arguments are objects

function test(person) { 
    person.age = 26 person = {
        name: 'yyy',
        age: 30
    }
    return person}const p1 = {
        name: 'yck',
        age: 25
    }
const p2 = test(p1)
console.log(p1) // -> ?
console.log(p2) // -> ?Copy the code

For the above code, can you write the result correctly? Let me break it down for you:

  • First, the function pass argument is a copy of the pointer to the passed object
  • When you go inside the function and modify the properties of the parameters, as I’m sure you know, the current value of P1 is also changed
  • But a split occurs when we reassign an object to Person

So person ends up with a new address (pointer) that has nothing to do with P1, resulting in the two variables having different values.

typeof vs instanceof

Typeof can correctly judge the type? How does instanceof correctly identify objects?

For primitive types, all but null can display the correct type typeof 1 //'number'
typeof '1' // 'string'
typeof undefined // 'undefined'
typeof true // 'boolean'
typeof Symbol() // 'symbol'Typeof [] //'object'
typeof {} // 'object'
typeof console.log // 'function'Copy the code

If we want to determine the correct type of an object, we can consider using Instanceof because the internal mechanism is determined by the prototype chain, and we will implement an Instanceof ourselves in later chapters.

const Person = function() {}
const p1 = new Person()
p1 instanceof
Person // true
var str = 'hello world'
str instanceof
String // false
var str1 = new String('hello world')
str1 instanceof
String // trueCopy the code

For primitive types, you can’t just use instanceof to determine the type, but there are ways to use instanceof to determine primitive types

class PrimitiveString {
    static [Symbol.hasInstance](x) {
        return typeof x === 'string'
    }
}
console.log('hello world' instanceof PrimitiveString) // trueCopy the code

In case you don’t know what Symbol. HasInstance is, it is something that allows us to customize the behavior of instanceof. This code is equivalent to Typeof ‘hello World ‘=== ‘string’, so it is true. This actually reflects a problem, instanceof is not 100% credible.

Type conversion

Cover questions: this knowledge is often seen in the pen questions, familiar with the conversion rules are not afraid of this kind of questions.

There are only three types of cast in JS. They are:

  • Convert to a Boolean value
  • Convert to number
  • Convert to string

Let’s look at a type conversion table before we get into the main story

Turns a Boolean

In a condition, all values except undefined, null, false, NaN, “, 0, -0 are converted to true, including all objects.

Object to primitive type

The built-in [[ToPrimitive]] function is called when an object converts its type. For this function, the algorithm logic generally looks like this:

· If it is already a primitive type, there is no need to cast

· Call x.valueof () and return the converted value if converted to the underlying type

· Call x.tostring () and return the converted value if converted to the base type

· If neither returns the original type, an error will be reported

Of course, you can override Symbol. ToPrimitive, which has the highest priority to call when converting to the original type.

let a = {
    valueOf() {
        return0},toString() {
        return '1'
    },
    [Symbol.toPrimitive]() {
        return 2
    }
}
1 + a // => 3Copy the code

Four operators

The addition operator differs from the other operators in that it has the following characteristics:

· If one of the operations is a string, the other will also be converted to a string

· If a party is not a string or a number, it is converted to a number or a string

1 + '1' // '11'
true + true // 2
4 + [1,2,3] //
"41, 2, 3"Copy the code

If you have any questions about the answer, read on.

  • For the first line of code, it fires feature one, so it converts the number 1 to a string, resulting in ’11’
  • For the second line of code, feature two fires, so convert true to the number 1
  • In the case of the third line of code, characteristic two is triggered, so the array is toString into strings 1,2,3, resulting in 41,2,3

The other thing to notice about addition is this expression ‘a’ + + ‘b’.

'a' + + 'b' // -> "aNaN"Copy the code

Because + ‘b’ equals NaN, the result is “aNaN”, and you may also see the form + ‘1’ in some code to quickly get the number type.

So for operators other than addition, as long as one side is a number, then the other side is converted to a number

4 * '3' // 12
4 * [] // 0
4 * [1, 2] // NaNCopy the code

Comparison operator

1. If it is an object, convert the object through toPrimitive

2. If it is a string, the comparison is made using the Unicode character index

let a = {
    valueOf() {
        return0},toString() {
        return '1'
    }
}
a > -1 // trueIn the above code, because A is an object, the value is converted to the original type by valueOf and then compared.Copy the code

this

How to correctly judge this? What’s this for the arrow function?

Many people confuse this concept, but it is not difficult at all. Many articles on the Internet make simple things complicated. In this section, you are sure to fully understand the concept of “this”.

Let’s start with a few function call scenarios

function foo() {
    console.log(this.a)
}
var a = 1
foo()
const obj = {
    a: 2,
    foo: foo
}
obj.foo()
const c = new foo()Copy the code

Let’s look at each of these scenarios one by one

  • For calling foo directly, no matter where foo is placed, this must be window, right
  • For obj.foo(), we just need to remember that whoever called the function is this, so this in the foo function is an obj object in this scenario
  • In the case of new, this is always bound to C and will not be changed in any way

So with that said, in a lot of code this should be fine, so let’s look at this in the arrow function, okay

function a() {
    return() = > {return () => {
            console.log(this)
        }
    }
}
console.log(a()()())Copy the code

First of all, there is no “this” in the arrow function. The “this” in the arrow function depends only on the “this” in the first normal function that wraps the arrow function. In this case, because the first normal function wrapping the arrow function is a, this is window. In addition, using a function such as bind for the arrow function is invalid.

The last case is a context-changing API like bind. For these functions, this depends on the first argument. If the first argument is null, it is window.

So speaking of bind, have you thought about, if you bind a function multiple times, what’s the context?

let a = {}
let fn = function () { console.log(this) }
fn.bind().bind(a)()
// => ?Copy the code

If you think the output is a, you’re wrong. We can actually convert this code to another form

/ / fn. The bind (). The bind is equal to (a)let fn2 = function fn1() {
    return function() {
        return fn.apply()
    }.apply(a)
}
fn2()Copy the code

As you can see from the code above, no matter how many times we bind the function, this in fn is always determined by the first bind, so the result is always window.

let a = { 
    name: 'yck' 
}
function foo() {
    console.log(this.name)
}
foo.bind(a)() // => 'yck'Copy the code

This is the rule for this, but it is possible for more than one rule to occur at the same time. In this case, the different rules will determine where this ends according to the highest priority.

First, new takes precedence, then bind, then obj.foo(), and finally foo. Once this is bound to the arrow function, it is not changed in any way.

summary

This is the first part of our JS basic knowledge. The knowledge points covered in this section are often seen in our daily development, and a lot of easy to appear pit also from these knowledge points, I believe that carefully read you will be in the future development of many pits. If you have any questions about this section, please let me know in the comments section.


JS basic knowledge and often test interview questions (2)

In this chapter we continue to understand some of the JS often tested and easily confused basic knowledge points.

== vs ===

Cover the area try: == and === what distinction does === have?

For ==, a cast is performed if the two types are different, which is what we discussed in the last chapter.

If we need to compare whether x and y are the same, we will proceed with the following judgment process:

1. First, it will judge whether the two types are the same. The same is the ratio size

2. If the type is different, then the type conversion will be performed

3. If null and undefined are being compared, return true

4. Check whether the two types are string and number. If yes, convert the string to number

5. 1 = = ‘1’

6. Left

7. 1 = = 1

8. Determine whether one of the parties is Boolean. If yes, the Boolean is converted to number

9. ‘1’ == true

10. Left

11. ‘1’ = = 1

12. Left

13. 1 = = 1

14. Determine whether one of the parties is object and the other is string, number, or symbol. If yes, convert the object to the original type

15. ‘1’ == { name: ‘yck’ }

16. Left

17. ‘1’ == ‘[object Object]’

Consider: after reading the above steps, for [] ==! [] Can you write the answer correctly?

Of course, this flow chart does not cover all the cases. I’ve only listed the cases that are commonly used, and you can refer to the standard documentation if you want to learn more.

It’s much simpler for ===, which is to determine whether the two types and values are the same.

closure

What is a closure?

The definition of A closure is simple: function A has A function B inside it, and function B can access variables in function A, so function B is A closure.

function A() {
    let a = 1
    window.B = function() {
        console.log(a)
    }
}
A()
B() // 1Copy the code

Many people interpret closures as functions that nest functions and then return a function. In fact, this explanation is incomplete, as my above example can refute this view.

In JS, closures exist to give you indirect access to variables inside a function.

Use closures in a loop to solve the problem of ‘var’ defining a function

for (var i = 1; i <= 5; i++) {
    setTimeout(function timer() {
        console.log(i)
    }, i * 1000)
}Copy the code

First of all, since setTimeout is an asynchronous function, it will execute the entire loop first, and then I will be 6, so it will print a bunch of 6’s.

There are three solutions. The first is the use of closures

for (var i = 1; i <= 5; i++) {
    (function(j) {
        setTimeout(function timer() {
            console.log(j)
        }, j * 1000)
    })(i)
}Copy the code

In the above code, we first use the immediate execution function to pass I into the function. At this time, the value is fixed on the parameter j and will not change. The next time the timer closure is executed, we can use the variable j of the external function to achieve this goal.

The second is to use the third argument of setTimeout, which is passed in as an argument to the timer function.

for (var i = 1; i <= 5; i++) {
    setTimeout(
        function timer(j) {
            console.log(j)
    },i * 1000,i)
}Copy the code

The third way is to use let definition I to solve the problem, and this is the most recommended way

for (let i = 1; i <= 5; i++) {
    setTimeout(function timer() {
        console.log(i)
    }, i * 1000)
}Copy the code

Depth copy

What is a shallow copy? How to implement shallow copy? What is a deep copy? How to implement deep copy?

In the previous chapter, we learned that an object type actually copies the address during assignment, resulting in a situation where everything else changes as well. Usually in development we don’t want to have this problem, we can use shallow copy to solve this problem.

let a = {
    age: 1
}
let b = a
a.age = 2
console.log(b.age) // 2Copy the code

Shallow copy

The first way to solve this problem is with object.assign, which many people think is for deep copies. No, object. assign will only copy all property values to the new Object. If the property value is an Object, it will copy the address, so it is not a deep copy.

let a = {

  age: 1

}

let b = Object.assign({}, a)

a.age = 2

console.log(b.age) // 1Copy the code

We can also use the expansion operator… To implement a shallow copy

let a = {
  age: 1
}
letb = { ... a } a.age = 2 console.log(b.age) // 1Copy the code

In general, shallow copies will solve most problems, but deep copies may be needed in the following situations

let a = {
    age: 1,
    jobs: {
        first: 'FE'}}letb = { ... a } a.jobs.first ='native'
console.log(b.jobs.first) // nativeCopy the code

The shallow copy only solves the first layer of the problem, and if the next value has an object in it, then it’s back to the beginning, both of which have the same address. To solve this problem, we need to use deep copy.

Deep copy

This problem can usually be solved with json.parse (json.stringify (object)).

let a = {
    age: 1,
    jobs: {
        first: 'FE'}}let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FECopy the code

But there are limitations to this approach:

  • Ignores undefined
  • Ignore the symbol
  • Cannot serialize function
  • Objects with circular references cannot be resolved

let obj = {
    a: 1,
    b: {
        c: 2,
        d: 3,
    },
}
obj.c = obj.b
obj.e = obj.a
obj.b.c = obj.c
obj.b.d = obj.b
obj.b.e = obj.b.c
let newObj = JSON.parse(JSON.stringify(obj))
console.log(newObj)Copy the code

If you have such a circular reference object, you will find that it is not possible to implement a deep copy through this method

The object cannot be serialized properly when a function, undefined, or symbol is encountered

let a = {
    age: undefined,
    sex: Symbol('male'),
    jobs: function() {},
    name: 'yck'
}
let b = JSON.parse(JSON.stringify(a))
console.log(b) // {name: "yck"}Copy the code

You’ll notice that in the above case, the method ignores functions and undefined.

But in general, complex data can be serialized, so this function solves most of the problems.

If the object you want to copy has built-in types and does not contain functions, you can use MessageChannel

function structuralClone(obj) {
    returnnew Promise(resolve => { const { port1, port2 } = new MessageChannel() port2.onmessage = ev => resolve(ev.data) port1.postMessage(obj) }) } var obj = { a: 1, b: {c: 2}} obj.b.d = obj.btest = async () => {
    const clone = await
    structuralClone(obj)
    console.log(clone)}test(a)Copy the code

Of course, you may want to himself to achieve a deep copy, but in fact it is difficult to achieve a deep copy, we need to consider various boundary conditions, such as the prototype chain how to deal with how to handle and so on, DOM, so here we implement deep copy is just simple version, and I actually more recommended lodash deep copy function.

function deepClone(obj) {
    function isObject(o) {
        return (typeof o === 'object' || typeof o === 'function') && o ! == null }if(! isObject(obj)) { throw new Error('Non-object')}let isArray = Array.isArray(obj)
    letnewObj = isArray ? [...obj] : {... obj } Reflect.ownKeys(newObj).forEach(key=> { newObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key] })return newObj
}
let obj = {
    a: [1, 2, 3],
    b: {
        c: 2,
        d: 3
    }
}
let newObj = deepClone(obj)
newObj.b.c = 1
console.log(obj.b.c) // 2Copy the code

The prototype

Question: How do you understand archetypes? How to understand the prototype chain?

When we create an object let obj = {age: 25}, we can see that there are many functions that can be used, but we haven’t defined them yet.

When you print obj in the browser, you will notice that there is also a __proto__ attribute on obj, so it seems that the previous question is related to this attribute.

Every JS object actually has a __proto__ attribute, which refers to the stereotype. This property is not recommended to use directly at this point, it’s just something the browser implemented in the early days to give us access to the internal property [[prototype]].

If we still don’t know what a prototype is, let’s see what __proto__ has in it.

By this point you can see that a prototype is also an object and contains many functions, so we can conclude that for obj, __proto__ can be used to find a prototype object that defines many functions for us to use.

We can also see a constructor property in the above diagram

Open the constructor property and we can see that there is also a prototype property with the same value as we saw earlier in __proto__. The constructor property refers back to the prototype, but not all functions have this property. Function.prototype.bind() does not have this property.

Prototypes are as simple as that, so let’s take a look at one more picture, which I believe will give you a thorough understanding of prototypes and prototype chains

After looking at this picture, let me explain what a prototype chain is. A prototype chain is a chain of objects connected by __proto__. The reason obj has access to valueOf is because obj found valueOf through the prototype chain.

The knowledge in this section can be summarized as follows:

  • Object is the father of all objects, and all objects can find it by __proto__
  • Function is the parent of all functions, and all functions can be found by __proto__
  • The function’s prototype is an object
  • The __proto__ attribute of an object points to the stereotype, and __proto__ connects the object to the stereotype to form the stereotype chain

If you want to learn more about prototypes, you can read my previous post

summary

So that’s all the common and confusing basics, and we’ll cover ES6 in the next chapter. If you have any questions about this section, please let me know in the comments section.

ES6 Knowledge points and often test the interview questions

We will look at ES6 later in this chapter.

Var, let, and const differences

What is promotion? What is a temporary dead zone? Var, let, and const?

In order to do that, you should first understand the concept of hoisting a power in the following case.

console.log(a) // undefined
var a = 1Copy the code

As we can see from the above code, we can use an undeclared variable even though it has not been declared. This situation is called promotion, and the promotion is the declaration.

In this case, we can look at the code like this

var a
console.log(a) // undefined
a = 1Copy the code

Let’s do another example

var a = 10
var a
console.log(a)Copy the code

For this example, if you think the value printed is undefined then you’re wrong, the answer should be 10. In this case, let’s look at the code this way

var a
var a
a = 10
console.log(a)Copy the code

So far, we’ve seen that variables declared by var can be promoted. In fact, not only variables can be promoted, but functions can also be promoted.

The console. The log / / ƒ (a)a() {}
function a() {}
var a = 1Copy the code

For the above code, the print result would be ƒ a() {}. Even if the variable is declared after the function, this indicates that the function will be promoted and take precedence over the variable promotion.

Var values are promoted to the top of the scope. Let’s look at let and const values.

Let’s start with an example:

var a = 1
let b = 1
const c = 1
console.log(window.b) // undefined
console.log(window. c) // undefined
function test(){
    console.log(a)
    let a
}
test(a)Copy the code

First, we declare variables in the global scope using let and const. The variables are not mounted to the window, which is different from var declarations.

Furthermore, if we use a before declaring a, we will report an error

You might think that there’s an upgrade happening here too, but for some reason it’s not accessible.

The reason for the error in the first place is because there is a temporary dead zone, and we cannot use variables before they are declared. This is one of the advantages that lets and const have over var. However, there is a difference between the promotion you think of and the promotion of var. Although variables are told at compile time that they are accessible in this scope, the access is restricted.

Var, let, and const are different from each other. The basic reason for the existence of upgrades is to solve the problem of functions calling each other

function test1() {
    test(2)}function test2() {
    test(1)}test(1)Copy the code

If the promotion did not exist, then the above code would not be implemented, because there is no way that test1 precedes Test2 and test2 precedes Test1.

Finally, let’s summarize the contents of this section:

· Function promotion takes precedence over variable promotion. Function promotion moves the entire function to the top of the scope. Variable promotion only moves the declaration to the top of the scope

  • Var has a boost that we can use before the declaration. Let, const cannot be used before a declaration because of a temporary dead zone
  • Var declaring variables in the global scope causes them to be mounted on window. The other two do not
  • Let and const act almost identically, except that the latter declares a variable that cannot be reassigned

Prototype inheritance and Class inheritance

Question: How does a prototype achieve inheritance? How does Class implement inheritance? What is the nature of Class?

There is no such thing as a class in JS. Class is just syntax sugar. It is essentially a function.

class Person {}
Person instanceof
Function // trueCopy the code

In the previous chapter, we discussed prototypes. In this section, we will use prototypes and classes to implement inheritance, respectively.

Combination of inheritance

Composite inheritance is the most common type of inheritance,

function Parent(value) {
    this.val = value
}
Parent.prototype.getValue= function() {
    console.log(this.val)
}
function Child(value) {
    Parent.call(this, value)
}
Child.prototype = new Parent()
const child = new Child(1)
child.getValue() // 1
child instanceof
Parent // trueCopy the code

The core method of inheritance is to inherit the property of the Parent class through the Parent.call(this) constructor of the child class, and then change the prototype of the child class to new Parent() to inherit the function of the Parent class.

The advantage of this inheritance method is that the constructor can pass parameters and will not be shared with the reference attribute of the parent class, and the function of the parent class can be reused. However, there is also a disadvantage that the constructor of the parent class is called when inheriting the function of the parent class, resulting in more unnecessary attributes of the parent class on the prototype of the child class, and there is a waste of memory.

Parasitic combination inheritance

This inheritance method optimizes combination inheritance. The disadvantage of combination inheritance is that it calls the constructor when inheriting the superclass function. We only need to optimize this.

function Parent(value) {
    this.val = value
}
Parent.prototype.getValue= function() {
    console.log(this.val)
}
function Child(value) {
    Parent.call(this, value)
}
Child.prototype = Object.create(Parent.prototype, {
    constructor: {
        value: Child,
        enumerable: false,
        writable: true,
        configurable: true
    }
})
const child = new Child(1)
child.getValue() // 1
child instanceof
Parent // trueCopy the code

The core of the above inheritance implementation is to assign the prototype of the parent class to the subclass, and set the constructor to the subclass, which not only solves the problem of useless superclass attributes, but also can correctly find the constructor of the subclass.

The Class inheritance

Both of these methods of inheritance are solved through prototypes. In ES6, we can use classes to implement inheritance, and it is very simple to implement

class Parent {
    constructor(value) {
        this.val = value
    }
    getValue() {
        console.log(this.val)
    }
}
class Child extends Parent {
    constructor(value) {
        super(value)
        this.val = value
    }
}
let child = new Child(1)
child.getValue() // 1
child instanceof
Parent // trueCopy the code

The core of class implementation inheritance is that the use of extends indicates which Parent class it inherits from, and that super must be called in the subclass constructor because this code can be thought of as parent-.call (this, value).

Of course, as mentioned earlier, there are no classes in JS. Classes are functions.

modular

Question: Why use modularity? What are the ways to achieve modularity and what are the characteristics of each?

There must be a reason to use a technology, so using modularity can give us the following benefits

  • Resolving name conflicts
  • Provide reusability
  • Improve code maintainability

Immediate execution function

In the early days, modularizing with immediate-execution functions was a common approach, and function scopes solved the problem of naming conflicts and fouling the global scope

(function(globalVariable){
    globalVariable.test = function() {} / /... Declare variables and functions that do not pollute the global scope}Copy the code

AMD and CMD

Since these two implementations are so rare these days, I won’t go into the details of the features, just understand how they are used.

// AMD
define(['./a'.'./b'].function(a, b) {/ / finished loading module can be used to a. d. o () o b.d ()}) / / CMD define (functionVar a = require(// exports, module) {var a = require(// exports, module) {'./a')
    a.doSomething()
})Copy the code

CommonJS

CommonJS was first used by Node and is still widely used today, for example in Webpack, but module management in Node is different from CommonJS.

// a.js
module.exports = {
    a: 1
}
// or 
exports.a = 1
// b.js
var module = require('./a.js')
module.a // -> logVar module = require(var module = require(var module = require(var module = require(var module = require(var module = require('./a.js'// exports = {a: module. Exports = {a: module.exports = {a: module. } var module = {id:'xxxx'// I have to know how to find him. Var exports = module.exports var load =function(module) {var a = 1 module.exports = areturnmodule.exports }; // Then find the unique // id when I require, and then wrap the thing I'm going to use in the immediate execute function, overCopy the code

Exports are similar to module. Exports, but you cannot directly assign a value to exports. Var exports = module.exports var exports = module.exports Exports no longer point to the same memory address. This change does not apply to module.exports.

ES Module

ES Module is a modular solution for a native implementation and differs from CommonJS in several ways

  • CommonJS supports dynamic import, i.e. Require (${path}/xx.js), which is currently not supported but has been proposed
  • CommonJS is a synchronous import, because it is used on the server and the files are local, so a synchronous import does not affect the main thread if it gets stuck. The latter is an asynchronous import, because for the browser, you need to download the file, and if you use synchronous import, it will have a big impact on the rendering
  • CommonJS is a copy of the value when exported. If the exported value changes, the imported value does not change, so if you want to update the value, you must import it again. However, the ES Module uses real-time binding. The import and export values point to the same memory address, so the import value changes with the export value
  • The ES Module is compiled as require/exports for execution

// Import module API import XXX from'./a.js'
import { XXX } from './a.js'// Export the module APIexport function a() {}
export default function() {}Copy the code

Proxy

What function can Proxy achieve?

If you’ve been following Vue, you probably know that in Vue3.0, Proxy will replace Object. DefineProperty to implement data response. Proxy is a new feature in ES6 that allows you to define operations from objects.

let p = new Proxy(target, handler)Copy the code

Target represents the object to which the agent is to be added, and handler is used to define operations within the object. For example, a set or get function can be used to define operations.

Next we implement a data response using a Proxy

let onWatch = (obj, setBind, getLogger)=> {
    let handler = {
        get(target, property, receiver) {
           getLogger(target, property)
            return Reflect.get(target, property, receiver)
        },
        set(target, property, value, receiver) {
            setBind(value, property)
            return Reflect.set(target, property, value)
        }
    }
    return new Proxy(obj, handler)
}
let obj = { a: 1 }
letP = onWatch(obj,(v, property) => {console.log(' listening to properties${property}Change for${v}`)
},(target, property) => {
        console.log(`'${property}' = ${target[property]}')}) p.a = 2 // listen to attribute a change p.a //'a' = 2Copy the code

In the above code, we have inserted our function logic into the original logic by custom set and get functions to notify when any property of an object is read or written.

Of course, this is the simple version of the responsive implementation. If we need to implement a responsive in Vue, we need to collect dependencies in GET and distribute updates in SET. The reason why Vue3.0 uses Proxy instead of the original API is that Proxy does not need to recursively add proxies for each property. The above operations can be completed in one time, and the performance is better. In addition, some data updates cannot be monitored in the original implementation, but the Proxy can perfectly monitor any data changes. The only drawback may be the poor compatibility of the browser.

map, filter, reduce

What role do map, filter, reduce have respectively?

Map creates a new array, iterates over the array, takes each element, performs some transformation and puts it into the new array.

[1, 2, 3]. Map (v => v + 1) // -> [2, 3, 4]'1'.'2'.'3'].map(parseInt)'1'ParseInt (0) -> 1'2', 1) -> parseInt(1)'3', 2) -> NaNCopy the code

The filter function also generates a new array. As we iterate through the array, we put the elements that return true into the new array. We can use this function to delete elements that are not needed

let array = [1, 2, 4, 6]
letnewArray = array.filter(item => item ! == 6) console.log(newArray) // [1, 2, 4]Copy the code

Like map, the filter callback takes three arguments and is used in the same way.

Finally, let’s talk about the Reduce part, which is the most difficult part to understand. Reduce converts the elements of an array to a value through a callback function.

If we want to implement a function that adds up all the elements in the function to get a value, we might write code like this

const arr = [1, 2, 3]
let total = 0
for (let i = 0; i < arr.length; i++) {
    total += arr[i]
}
console.log(total) //6Copy the code

However, if we use Reduce, we can optimize the traversal part to a single line of code

const arr = [1, 2, 3]
const sum = arr.reduce((acc, current) =>
acc + current, 0)
console.log(sum)Copy the code

In the case of Reduce, it takes two parameters, the callback function and the initial value. Let’s break down the process of Reduce in the above code

  • The initial value is 0, which is passed as the first argument when the first callback is executed
  • The callback function takes four parameters: the cumulative value, the current element, the current index, and the original array
  • On the first callback, the current value is added to the initial value to give the result 1, which is passed in as the first argument on the second callback
  • So the second time the callback is executed, the values add up to 1 and 2, and so on. At the end of the loop, you get 6

After the above analysis, you should understand how reduce transforms all elements into a value through the callback function. Of course, Reduce can also achieve many functions. Next, we can implement map function through Reduce

const arr = [1, 2, 3]
const mapArray = arr.map(value => value * 2)
const reduceArray = arr.reduce((acc, current)=> {
    acc.push(current * 2)
    return acc
}, [])
console.log(mapArray, reduceArray) // [2, 4, 6]Copy the code

If you are still confused by this implementation, you can follow the previous parsing steps to analyze the process.

summary

In this chapter we have covered some of the common ES6 topics, and we will cover the rest of the asynchronous topics in the next chapter. If you have any questions about this section, please let me know in the comments section.

JS asynchronous programming and often test the interview questions

In the previous chapter we looked at some common ES6 grammar. In this chapter, we will learn the content of asynchronous programming. In view of the importance of asynchronous programming in JS, we will use three chapters to learn the key and difficult points involved in asynchronous programming. At the same time, this part of the content is also the scope of the interview.

Concurrency is different from parallelism

Scope test questions: the difference between concurrent and parallel?

Asynchronous is not the same as this section, but the two terms are often confused. In fact, the reason for the confusion may simply be the similarity of the two nouns in Chinese, but they are completely different words in English.

Concurrency is A macro concept. I have task A and task B, and I complete them by switching between tasks over A period of time. This is called concurrency.

Parallelism is A micro concept. If there are two cores in the CPU, I can do tasks A and B at the same time. Multitasking is called parallelization.

Callback function

What is a callback function? What are the drawbacks of callback functions? How to solve the callback hell problem?

The following code is an example of a callback function:

Ajax (URL, () => {// processing logic})Copy the code

But callbacks have a fatal weakness: they are easy to write Callback hell. Assuming multiple requests have dependencies, you might write something like this:

Ajax (url, () = > {/ / processing logic ajax (url1, () = > {/ / processing logic ajax (url2, () = > {/ / processing logic})})})Copy the code

The above code may seem unreadable and unmaintainable, but of course, you might be tempted to say that this is not an easy problem to solve, just write the functions separately

function firstAjax() {ajax(url1,() => {// Process logic secondAjax()})}function secondAjax() {ajax (url2, () = > {/ / processing logic})} ajax (url, () = > {/ / processing logic firstAjax ()})Copy the code

The above code looks good to read, but it doesn’t solve the underlying problem.

The fundamental problem with callback hell is this:

1. Nested functions are coupled, so if you change them, they change the whole thing

2. Many nested functions make it difficult to handle errors

Of course, callbacks have several other disadvantages, such as the inability to catch an error using a try or catch, and the inability to return directly. In the next few sections, we’ll look at other techniques to solve these problems.

Generator

What do you understand by Generator?

One of the most difficult concepts to understand in ES6 is the Generator, which is characterized by its ability to control the execution of functions. We won’t go into what a Generator is in this section, but will focus on some of the confusing aspects of it.

function *foo(x) {
    let y = 2 * (yield (x + 1))
    let z = yield (y / 3)
    return (x + y + z)
}
let it = foo(5)
console.log(it.next())   // => {value: 6, done: false}
console.log(it.next(12)) // => {value: 8,
done: false}
console.log(it.next(13)) // => {value:
42, done: true}Copy the code

You may be wondering why the value is different from what you expected, so let me break it down line by line for you

  • First, Generator function calls, unlike normal functions, return an iterator
  • When the first next is performed, the pass is ignored and the function is paused at yield (x + 1), so returns 5 + 1 = 6
  • When next is executed a second time, the argument passed is equal to the value returned by the previous yield. If you do not pass any arguments, yield always returns undefined. Let y be equal to 2 times 12, so the second yield is equal to 2 times 12 over 3, which is 8
  • When next is executed the third time, the passed argument is passed to z, so z = 13, x = 5, y = 24, which adds up to 42

Generator functions are generally not seen much, in fact, it is also a bit around the relationship, and generally with the CO library to use. Of course, we can solve the problem of callback hell with the Generator function. We can rewrite the previous callback hell example as follows:

function *fetch() {
    yield ajax(url, () => {})
    yield ajax(url1, () => {})
    yield ajax(url2, () => {})
}
let it = fetch()
let result1 = it.next()
let result2 = it.next()
let result3 = it.next()Copy the code

Promise

What are the characteristics of Promise? What are the advantages and disadvantages? What is a Promise chain? What is the difference between a Promise constructor execution and a THEN execution?

A Promise is a Promise that promises a definite answer in the future. It comes in three states:

1. Pending

2. Resolved

3. I don’t like it.

This promise can never be changed once it has gone from waiting to something else, meaning once it has become resolved, it cannot be changed again

new Promise((resolve,reject) => {
    resolve('success'// reject(reject)'reject')})Copy the code

When we construct a Promise, the code inside the constructor executes immediately

new Promise((resolve, reject) => {
    console.log('new Promise')
    resolve('success')
})
console.log('finifsh')
// new Promise
-> finifshCopy the code

Promise implements chained calls, which means that after each call to THEN, a Promise is returned, and it is a completely new Promise, also because the state is immutable. If you use return in then, the value of return is wrapped by promise.resolve ()

Promise.resolve(1)
.then(res => {
    console.log(res) // => 1
    return}).then(res => {console.log(res) // => 2})Copy the code

Of course, Promise also solves the problem of callback hell very well. You can rewrite the previous callback hell example as follows:

ajax(url)
.then(res => {
    console.log(res)
    return ajax(url1)
}).then(res => {
    console.log(res)
    return ajax(url2)
}).then(res => console.log(res))Copy the code

While I’ve talked about some of the advantages and features of promises, there are also some disadvantages, such as the inability to cancel promises and the need to catch errors through callbacks.

Async and await

What are the advantages and disadvantages of async and await? What is the principle of await?

If async is added to a function, the function returns a Promise

async function test() {
    return "1"
}
console.log(test()) // -> Promise {<resolved>:"1"}Copy the code

Async wraps the return value of the function in promise.resolve (), the same way that then handles the return value, and await can only be used with async

async function test() {
    let value = await sleep()
}Copy the code

Async and await are the ultimate solution to asynchron. The advantage of handling the then call chain is that it is easier to write code cleanly and accurately than using Promises directly, since it is also very nasty to write a lot of THEN calls, and it also solves the callback hell problem gracefully. There are, of course, some disadvantages, as await transforms asynchronous code into synchronous code, which can result in performance degradation if multiple asynchronous codes have no dependencies but use await.

async function test() {// the following code can be used in promise. all mode if there are no dependencies // If there are dependencies, Await fetch(URL) await fetch(url1) await fetch(url2)}Copy the code

Here’s an example of using await:

let a = 0
let b = async () => {
    a = a + await 10
    console.log('2', a) // -> '2' 10
}
b()
a++
console.log('1', a) // -> '1' 1Copy the code

You may be confused by the above code, let me explain why

  • First function b is executed and variable a remains 0 until it reaches await 10. Because await internally implements a generator, which keeps things on the stack, a = 0 is saved at this time
  • Since the await is an asynchronous operation, subsequent expressions that do not return a Promise will be wrapped as promise.reslove (return value) and then execute the synchronized code outside the function
  • After the synchronous code is executed, the asynchronous code is executed, and the saved value is used. At this time, a = 0 + 10

The explanation above mentioned that await internally implements the generator, whereas await is the syntax-sugar of a generator plus a Promise and internally implements the automatic execution of the generator. If you are familiar with CO, you can actually achieve such grammatical sugar yourself.

Common timer functions

What are the characteristics of setTimeout, setInterval, requestAnimationFrame?

Asynchronous programming, of course, is not the timer, the common timer functions are setTimeout, setInterval, requestAnimationFrame. Let’s start with the most common one, setTimeout. A lot of people think setTimeout is how long, so it should be how long.

This is wrong, because JS is executed in a single thread, and if the previous code affects performance, the setTimeout will not be executed as scheduled. Of course, we can fix the setTimeout in code to make the timer relatively accurate

let period = 60 * 1000 * 60 * 2
let startTime = new Date().getTime()
let count = 0
let end = new Date().getTime() + period
let interval = 1000
let currentInterval = interval
function loop() {count++ // the time elapsed for code executionlet offset = new Date().getTime() - (startTime + count *interval);
    let diff = end - new Date().getTime()
    let h = Math.floor(diff / (60 * 1000 * 60))
    let hdiff = diff % (60 * 1000 * 60)
    let m = Math.floor(hdiff / (60 * 1000))
    let mdiff = hdiff % (60 * 1000)
    let s = mdiff / (1000)
    let sCeil = Math.ceil(s)
    letCurrentInterval = interval-offset console.log()':'+h, ':'+m, '毫秒:'+s, 'Round up second:'+sCeil, Code execution time:+offset, 'Next cycle interval'+currentInterval) 
    setTimeout(loop, currentInterval)
}
setTimeout(loop,currentInterval)Copy the code

Now let’s look at setInterval, which is basically the same function as setTimeout, except that it executes a callback every once in a while.

In general, setInterval is not recommended. First, like setTimeout, there is no guarantee that the task will be executed at the expected time. Second, it has the problem of performing accumulation, as shown in the pseudocode below

function demo() {
    setInterval(function(){
        console.log(2)
    },1000)
    sleep(2000)
}
demo()Copy the code

In the browser environment, if there is a time-consuming operation during the execution of the timer, multiple callbacks will be executed at the same time after the time-consuming operation has finished, which may cause performance problems.

If you need a loop timer, you can do it in requestAnimationFrame

function setInterval(callback, interval) {
    let timer
    const now = Date.now
    let startTime = now()
    let endTime = startTime
    const loop = () => {
        timer = window.requestAnimationFrame(loop)
        endTime = now()
        if (endTime - startTime >=interval) {
            startTime = endTime = now()
            callback(timer)
        }
    }
timer = window.requestAnimationFrame(loop)
    return timer
}
let a = 0
setInterval(timer=> {
    console.log(1)
    a++
    if (a === 3) cancelAnimationFrame(timer)
}, 1000)Copy the code

First, requestAnimationFrame has its own function throttling function, which can be executed only once in 16.6 milliseconds (without losing frames), and the delay effect of this function is accurate. There are no other timer errors. Of course, you can also use this function to implement setTimeout.

summary

Asynchronous programming is a difficult content to master in JS, but also a very important knowledge point. If you have any questions about this section, please feel free to let me know in the comments section.

JS advanced knowledge and often test interview questions

In this chapter, we will learn some knowledge related to the principle, will not explain the role and usage of the knowledge points involved, if you are not familiar with these content, it is recommended to learn the relevant knowledge points before learning the principle of knowledge.

Write the call, apply, and bind functions

What are the internal implementations of the call, apply, and bind functions?

First of all, consider how to implement these functions

  • If the first argument is not passed, the context defaults to window
  • Changed the reference to this so that new objects can execute the function and accept arguments

So let’s implement call first

Function.prototype.myCall = function(context){
    if(typeof this ! = ='function') {
        throw new TypeError('Error') } context = context || window context.fn = this const args = [...arguments].slice(1) const result = context.fn(... args) delete context.fnreturn result
}Copy the code

Here’s an analysis of the implementation:

  • First, context is optional. If not passed, the default context is window
  • Next, create an FN attribute for the context and set the value to the function that needs to be called
  • Because call can pass multiple arguments as arguments to the calling function, you need to strip out the arguments
  • The function is then called and the function is removed from the object

Above is the idea of the implementation of call, apply implementation is similar, the difference lies in the treatment of parameters, so it is not an analysis of the train of thought

Function.prototype.myApply = function(context){
    if(typeof this ! = ='function') {
        throw new TypeError('Error')
    }
    context = context || window
    context.fn = this
    letResult // Handles parameters differently than callif(arguments[1]) { result = context.fn(... arguments[1]) }else {
        result = context.fn()
    }
    delete context.fn
    return
    result
}Copy the code

The implementation of Bind is slightly more complicated than the other two functions because bind needs to return a function and has to determine some boundary issues. Here is the implementation of bind

Function.prototype.myBind = function(context) {
    if(typeof this ! = ='function') {
        throw new TypeError('Error'} this = this const args = [...arguments].slice(1) // return a functionreturn function F() {// we can return a function, so we need to judgeif (this instanceof F){
            returnnew _this(... args,... arguments) }return_this.apply(context, args.concat(... arguments)) } }Copy the code

Here’s an analysis of the implementation:

  • The first steps are similar to the previous implementation, so I won’t go into detail
  • Bind returns a function. There are two ways to call a function. One is to call it directly, and the other is to call it via new
  • For direct calls, apply is selected, but for parameters, note the following: Since bind can implement a code like f.bind(obj, 1)(2), we need to concatenate the arguments on both sides, hence the implementation args.concat(… arguments)
  • And finally, with new, in the previous chapter we learned how to say this, in the case of new, it’s not going to change this in any way, so in this case we need to ignore this, right

new

What is the principle of new? What’s the difference between creating an object as a new object and creating it as a literal?

Four things happen during the call to new:

1. Freshmen become an object

2. Link to the prototype

3. The binding this

4. Return the new object

Based on the above process, we can also try to implement a new ourselves

function create() {
    let obj = {}
    let Con = [].shift.call(arguments)
    obj.__proto__ = Con.prototype
    let result = Con.apply(obj, arguments)
    return result instanceof Object ? result : obj
}Copy the code

Here’s an analysis of the implementation:

  • Create an empty object
  • Getting the constructor
  • Sets the prototype for the empty object
  • Bind this and execute the constructor
  • Make sure the return value is an object

In the case of an object, it’s all generated by new, whether it’s function Foo() or let a = {b: 1}.

For creating an object, it is more recommended to create the object in a literal way (both for performance and readability). Because you create objects with new Object(), you need to find objects through the scope chain, but you don’t have that problem with literals.

function Foo() {} / /functionIt's a syntactic sugar // internally equivalent to new Function()letA = {b: 1} // This literal also uses new Object() internally. For more information about new, read my post about the new operator.Copy the code

The principle of instanceof

How does instanceof work?

Instanceof can correctly determine the type of the object, because the internal mechanism is to determine whether the prototype of the type can be found in the prototype chain of the object.

We can also try to implement instanceof

function myInstanceof(left, right) {
    let prototype = right.prototype
    left = left.__proto__
    while (true) {
        if (left === null || left === undefined)
        return false
        if (prototype === left)
        return true
        left = left.__proto__
    }
}Copy the code

Here’s an analysis of the implementation:

  • First get the prototype of the type
  • Then get the prototype of the object
  • Then we keep iterating to see if the stereotype of the object is equal to the stereotype of the type until the stereotype of the object is null, because the stereotype chain ends up being null

Why 0.1 + 0.2! = 0.3

Involved area test questions: why 0.1 + 0.2! = 0.3? How to solve this problem?

First, the reason is that JS uses IEEE 754 double precision version (64-bit), and any language that uses IEEE 754 has this problem.

We all know that computers store things in binary, so 0.1 is represented in binary as

0.1 = 2^-4 * 1.10011(0011)Copy the code

We can see that 0.1 is a number that loops indefinitely in binary, not just 0.1, but many decimal numbers loop indefinitely in binary. That’s fine, but the JS floating-point standard cuts our numbers.

The IEEE 754 double precision version (64-bit) divides 64-bit into three segments

  • The first digit is used to indicate the sign
  • The next 11 bits represent the exponent
  • The other bits are used to represent the significant bits, i.e., 10011(0011) in 0.1 in binary.

The number of these loops is clipped, and the accuracy is lost, so that 0.1 is no longer 0.1, but instead becomes 0.100000000000000002

= = = 0.1/0.100000000000000002 /trueCopy the code

Similarly, 0.2 is infinite in binary and loses its precision after being clipped to 0.200000000000000002

= = = 0.2/0.200000000000000002 /trueCopy the code

So the two add up not to 0.3 but to 0.300000000000000004

0.1 + 0.2 === 0.30000000000000004 // trueCopy the code

If 0.1 is not 0.1, then why is console.log(0.1) correct?

Since the binary is converted to decimal, which is converted to string, and an approximation occurs during the conversion, the printed value is actually an approximation, which you can verify by using the following code

The console. The log (0.100000000000000002) / / 0.1Copy the code

So with that said, let’s finally talk about how to solve this problem. In fact, there are many ways to solve the problem, here we choose the original way to provide the simplest solution to the problem

ParseFloat ((0.1 + 0.2).tofixed (10)) === 0.3 //trueCopy the code

Garbage collection mechanism

V8 under the garbage collection mechanism is what?

V8 realizes accurate GC, and GC algorithm adopts generational garbage collection mechanism. Therefore, V8 divides memory (the heap) into young and old.

Cenozoic algorithm

Exploiture GC algorithm is used to avenge an object in the Cenozoic.

In the new generation space, the memory space is divided into two parts, which are From space and To space. Of these two Spaces, one must be used and the other free. The newly allocated objects are put into the From space, and when the From space is full, the new generation GC starts. The algorithm checks for surviving objects in the From space and copies them into the To space, and destroys inanimate objects. When replication is complete, the From space and To space are interchanged, and GC is finished.

Old generation algorithm

The objects in the old generation generally have a long life and a large number of objects. Two algorithms are used, namely, the marker clearing algorithm and the marker compression algorithm.

Before we talk about algorithms, let’s talk about what happens when an object appears in an old generation space:

  • Be insane. If an object in the new generation has experienced the Scavenge algorithm, it will move the object from the new generation space to the old generation space.
  • The size of objects in the To space exceeds 25%. In this case, objects are moved from the new generation space to the old generation space in order not to affect memory allocation.

The space in the old generation is very complex, there are several Spaces as follows

enum AllocationSpace {
    // TODO(v8:7464): Actually map this space's memory as read-only. RO_SPACE, // immutable object space NEW_SPACE, // New generation space for GC replication algorithm OLD_SPACE, // old generation resident object space CODE_SPACE, // old code object space MAP_SPACE, // old map object LO_SPACE, // old large space object NEW_LO_SPACE, // New large space object FIRST_SPACE = RO_SPACE, // new large space object FIRST_SPACE = RO_SPACE, LAST_SPACE = NEW_LO_SPACE, FIRST_GROWABLE_PAGED_SPACE = OLD_SPACE, LAST_GROWABLE_PAGED_SPACE = MAP_SPACE };Copy the code

In the old generation, the marker clearing algorithm is started first in the following cases:

  • When a space is not partitioned
  • The number of objects in the space exceeds a certain limit
  • Space does not guarantee that objects in the new generation will move into the old generation

In this phase, all objects in the heap are traversed, live objects are marked, and all unmarked objects are destroyed when the marking is complete. When marking large pairs of memory, it can take hundreds of milliseconds to complete a single marking. This can lead to some performance problems. To address this, in 2011, V8 switched from the stop-the-world flag to the incremental flag. During incremental markup, GC breaks down the markup work into smaller modules, allowing THE JS application logic to execute for a while between modules without causing the application to stop. But in 2018, THERE was another major breakthrough in GC technology, called concurrent tagging. This technique allows GC to scan and tag objects while allowing JS to run, which you can read more about by clicking on this blog.

Clearing objects will cause heap memory fragmentation, when fragmentation exceeds a certain limit will start compression algorithm. During compression, live objects are moved from one end to the other until all the objects are moved and then the memory is cleared.

summary

The above is the content of JS advanced knowledge points, this part of the knowledge compared to the content before more in-depth and more theory, but also in the interview can open the gap between other candidates a piece of content. If you have any questions about this section, please let me know in the comments section.

JS to consider

We went through seven chapters to learn about this part of JS, so next, we will make sure that you understand this part in the form of a few questions.

This will not only deepen your understanding, but also help you connect the dots. Once you have the ability to connect the dots, you won’t have to ask and answer questions very often. If you can add some relevant points to every question the interviewer asks you, you’ll be more likely to raise your rating.

Consider a question: JS is divided into which two big types? What are the characteristics of each? How do you determine the right type?

First of all, many people can answer these questions very well. Next, I will give you a little idea to tell something different.

Thinking guide:

1. For primitive types, you can point out some problems with null and number. For object types, you can start from the garbage collection point of view, or you can say that object types have a shallow copy problem.

2. For judging types, you can compare the difference between typeof and instanceof and point out that instanceof is not entirely accurate.

Above is the answer train of thought of this question, of course, not to say that everyone completely according to this train of thought to answer, but there is a consciousness, when answering the interview questions, try to extend some pit of this knowledge point or something related to this knowledge point.

Question 2: What do you understand as a prototype?

Thinking guide:

At least say the summary in the prototype section, and then point out some small points, such as not all functions have the prototype property, and then extend the concept of prototype chain, how to use prototypes to implement inheritance, and then extend the ES6 class to implement inheritance.

What is the difference between bind, call and apply?

Thinking guide:

The first is to say the difference between the three, if you have implemented one of the functions, you can try to say their ideas. And then you can talk a little bit about what “this” is, and there are a couple of rules about what “this” is, and the “this” rule involves new, so finally you can talk about what you think new is.

Question 4: What has been used in ES6?

Thinking guide:

There’s so much to say here, you can list one or two points. Let’s say class, which brings us back to the prototype; Say promise, and the line is pulled to the asynchronous content; You can talk about proxy, so if you’ve used the Vue framework, you can talk about reactive principles; You can also talk about the syntax for declaring variables like let, so you can talk about the difference with var, and the promotion part.

Question 5: How does JS work?

Thinking guide:

This is actually a huge piece of content. You can start by saying that JS runs in a single thread, and here you can explain the difference between a thread and a process. The difference between a microtask and a macro task, which is a microtask and which is a macro task, the difference between browser and Node Eventloop, and finally the garbage collection in JS.

summary

Although the question is not much, but in fact, behind each question can be extended a lot of content, we should always have a consciousness in the process of learning, you learn this piece of content in the end and which knowledge point in your mind is related. You are also welcome to summarize these questions and link to the summary in the comments. I will select good articles and put them in a separate chapter for you to refer to.

DOM breakpoint

You’ve all heard of javascript breakpoints, but DOM breakpoints are less familiar. If you want to see how a DOM element is changed through JAVASCRIPT, you can use this function.

When we add this breakpoint to ul, if we change the number of ul children, for example, we will automatically jump to the corresponding JS code

In fact, we can not only give DOM interrupt points, we can also give Ajax or Event Listener interrupt points.

View events

You can also use DevTools to see how many events have been added to the page. If you have a performance problem with scrolling, you can check how many Scroll events have been added

Find the one we checked before DOM The element

I don’t know if you’ve ever had a problem where you can’t find the DOM elements that you looked at before, and you have to go through them one by one and it’s a little tricky, but that’s where you can use this feature.

We can use $0 to find the last DOM element we looked at, $1 to find the last DOM element, and so on. Now, you might say, well, what’s the use of printing out elements, where do you need to find them, don’t worry, I can solve this problem right now

When you click on this option, the page immediately jumps to where the element is, and DevTools changes to the Elements TAB.

Debugging

Give JS interrupt point surely everyone will, but interrupt point also has a unknown Tips.

for (letindex = 0; index < 10; Index ++) {// all logical console.log(index)}Copy the code

For this code, if I only want to see the breakpoint information when index is 5, but once I break it, the loop will stop every time, which is a waste of time, then this little trick will solve the problem perfectly



First we right-click the breakpoint and select Edit Breakpoint… options

Enter index === 5 in the popup box. The breakpoint will turn orange and will only be executed if it matches the expression

summary

Although the content of this chapter is not much, but the several scenes involved are often encountered in daily life, I hope the content of this chapter will be helpful to you.