Interviewer: What about data types in JavaScript? Storage differences?
preface
In JavaScript, we can divide into two types:
- Basic types of
- The complex type
The difference between the two types is that the storage location is different
First, basic types
The basic types are as follows:
- Number
- String
- Boolean
- Undefined
- null
- symbol
Number
The most common integer type format for numeric values is decimal. Octal (starting with zero) and hexadecimal (starting with 0x) can also be set
let intNum = 55 // Base 10 of 55
let num1 = 070 // Base 56
let hexNum1 = 0xA // hexadecimal 10
Copy the code
Floating-point types must contain a decimal point in the sum of values and can also be represented by scientific notation
let floatNum1 = 1.1;
let floatNum2 = 0.1;
let floatNum3 = 1.; // Valid, but not recommended
let floatNum = 3.125 e7; / / equal to 31250000
Copy the code
Within numeric types, there is a special value NaN, which means “not a number” and is used to indicate that the operation that was supposed to return a number failed (rather than throw an error)
console.log(0/0); // NaN
console.log(-0/+0); // NaN
Copy the code
Undefined
Undefined has only one value, which is the special value Undefined. When a variable is declared using var or let but not initialized, it is equivalent to giving the variable a undefined value
let message;
console.log(message == undefined); // true
Copy the code
There is a difference between a variable that contains undefined and an undefined variable
let message; // This variable is declared with undefined
console.log(message); // "undefined"
console.log(age); // This variable is not declared
Copy the code
String
Strings can be marked with double quotation marks (“), single quotation marks (‘), or backquotation marks (‘)
let firstName = "John";
let lastName = 'Jacob';
let lastName = `Jingleheimerschmidt`
Copy the code
Strings are immutable, meaning that their values cannot change once they are created
let lang = "Java";
lang = lang + "Script"; // Destroy before create
Copy the code
Null
The Null type also has only one value, the special value Null
Logically, a null value represents an empty object pointer, which is why passing a NULL to typeof returns “object”
let car = null;
console.log(typeof car); // "object"
Copy the code
Undefined is derived from null
console.log(null= =undefined); // true
Copy the code
Whenever a variable wants to hold an object and there is no object available at the time, null can be used to populate the variable
Boolean
Boolean types have two literals: true and false
Boolean allows you to convert other types of data to Booleans
Here are the rules:
The data type is converted totrueThe value of is converted tofalseThe value of theStringNon-empty string""
NumberNon-zero values (including infinity)0 、 NaN
ObjectAny objectnullUndefined N/A (not present)undefined
Copy the code
Symbol
The Symbol is a primitive value and the Symbol instance is unique and immutable. The purpose of the symbol is to ensure that object attributes are uniquely identified without the risk of attribute collisions
let genericSymbol = Symbol(a);let otherGenericSymbol = Symbol(a);console.log(genericSymbol == otherGenericSymbol); // false
let fooSymbol = Symbol('foo');
let otherFooSymbol = Symbol('foo');
console.log(fooSymbol == otherFooSymbol); // false
Copy the code
2. Reference types
Complex types are collectively called Object, and we mainly describe the following three types:
- Object
- Array
- Function
Object
The common way to create an object is the literal representation of an object. The property name can be a string or a value
let person = {
name: "Nicholas"."age": 29.5: true
};
Copy the code
Array
A JavaScript array is an ordered set of data, but unlike other languages, each slot in the array can store any type of data. Also, arrays are dynamically sized, growing automatically as data is added
let colors = ["red".2, {age: 20 }]
colors.push(2)
Copy the code
Function
Functions are actually objects, and each Function is an instance of Function, which also has properties and methods, just like any other reference type
There are three common expressions for functions:
- Function declaration
// Function declaration
function sum (num1, num2) {
return num1 + num2;
}
Copy the code
- Functional expression
let sum = function(num1, num2) {
return num1 + num2;
};
Copy the code
- Arrow function
Function declaration and function expression in two ways
let sum = (num1, num2) = > {
return num1 + num2;
};
Copy the code
Other reference types
In addition to the three mentioned above, there are also Date, RegExp, Map, Set, etc……
Three, storage differences
Base data types and reference data types are stored differently in memory:
-
Basic data types are stored in stacks
-
Objects of reference type are stored in the heap
When we assign a variable to a variable, the first thing the parser needs to determine is whether the value is a primitive or a reference type
Here’s an example
Basic types of
let a = 10;
let b = a; // Assign
b = 20;
console.log(a); / / 10 values
Copy the code
The value of A is a basic type. It is stored on the stack, and the value of A is assigned to B. Although the two variables have the same value, the two variables hold two different memory addresses
The following illustration illustrates the basic type assignment:
Reference types
var obj1 = {}
var obj2 = obj1;
obj2.name = "Xxx";
console.log(obj1.name); // xxx
Copy the code
Reference type data is stored internally, and each heap memory has a reference address, which is stored on the stack
Obj1 is a reference type, summarized during assignment, which actually copies the stack reference address of the heap object to obj2. In fact, they all refer to the same heap object, so changing obj2 will affect obj1
The following figure illustrates the reference type assignment process
summary
- Different allocation of memory addresses when declaring variables:
- Values of simple types are stored on the stack, and the corresponding values are stored on the stack
- The value corresponding to the reference type is stored in the heap, and the address pointing to the heap memory is stored in the stack
- Different types of data result in different variable assignments:
- Simple type assignment, which generates the same value, two objects corresponding to different addresses
- Complex type assignment assigns the memory address of an object to another variable. That is, two variables refer to the same object in heap memory
Interviewer: What’s the difference between deep copy and shallow copy? How to implement a deep copy?
Data type storage
As mentioned in the previous article, there are two main data types in JavaScript:
- Basic types of
- Reference types
Basic type data is stored in stack memory
Reference type data is stored in heap memory, and the variable of a reference data type is a reference to an actual object in heap memory, which is stored on the stack
Shallow copy
Shallow copy refers to the creation of new data that has an exact copy of the original data attribute values
If the property is of a primitive type, the value of the primitive type is copied. If the attribute is a reference type, the memory address is copied
That is, shallow copies copy a layer, and deep reference types share memory addresses
Let’s simply implement a shallow copy
function shallowClone(obj) {
const newObj = {};
for(let prop in obj) {
if(obj.hasOwnProperty(prop)){ newObj[prop] = obj[prop]; }}return newObj;
}
Copy the code
In JavaScript, shallow copies exist:
Object.assign
Array.prototype.slice()
.Array.prototype.concat()
- Replication using extended operators
Object.assign
var obj = {
age: 18.nature: ['smart'.'good'].names: {
name1: 'fx'.name2: 'xka'
},
love: function () {
console.log('fx is a great girl')}}var newObj = Object.assign({}, fxObj);
Copy the code
slice()
const fxArr = ["One"."Two"."Three"]
const fxArrs = fxArr.slice(0)
fxArrs[1] = "love";
console.log(fxArr) // ["One", "Two", "Three"]
console.log(fxArrs) // ["One", "love", "Three"]
Copy the code
concat()
const fxArr = ["One"."Two"."Three"]
const fxArrs = fxArr.concat()
fxArrs[1] = "love";
console.log(fxArr) // ["One", "Two", "Three"]
console.log(fxArrs) // ["One", "love", "Three"]
Copy the code
Extended operator
const fxArr = ["One"."Two"."Three"]
const fxArrs = [...fxArr]
fxArrs[1] = "love";
console.log(fxArr) // ["One", "Two", "Three"]
console.log(fxArrs) // ["One", "love", "Three"]
Copy the code
Deep copy
Deep copy opens up a new stack. Two object families are identical but correspond to two different addresses. Modifying the properties of one object does not change the properties of the other
Common deep-copy modes are:
-
_.cloneDeep()
-
jQuery.extend()
-
JSON.stringify()
-
Handwriting loop recursion
_.cloneDeep()
const _ = require('lodash');
const obj1 = {
a: 1.b: { f: { g: 1}},c: [1.2.3]};const obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);// false
Copy the code
jQuery.extend()
const$=require('jquery');
const obj1 = {
a: 1.b: { f: { g: 1}},c: [1.2.3]};const obj2 = $.extend(true, {}, obj1);
console.log(obj1.b.f === obj2.b.f); // false
Copy the code
JSON.stringify()
const obj2=JSON.parse(JSON.stringify(obj1));
Copy the code
However, this approach has the disadvantage of ignoring undefined, symbol, and functions
const obj = {
name: 'A'.name1: undefined.name3: function() {},
name4: Symbol('A')}const obj2 = JSON.parse(JSON.stringify(obj));
console.log(obj2); // {name: "A"}
Copy the code
Cycle recursion
function deepClone(obj, hash = new WeakMap(a)) {
if (obj === null) return obj; // If it is null or undefined, I will not copy it
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
// May be objects or ordinary values if functions do not need deep copies
if (typeofobj ! = ="object") return obj;
// Make a deep copy of an object
if (hash.get(obj)) return hash.get(obj);
let cloneObj = new obj.constructor();
// Find the constructor from the parent class stereotype, which points to the current class itself
hash.set(obj, cloneObj);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// Implement a recursive copycloneObj[key] = deepClone(obj[key], hash); }}return cloneObj;
}
Copy the code
Fourth, the difference between
Here are two images to help you see the difference between a shallow copy and a deep copy
As you can see from the above figure, both shallow and deep copies create a new object, but behave differently when copying object properties
Shallow copy only copies the pointer that the attribute points to an object, but not the object itself. The old and new objects still share the same memory. Modifying object attributes affects the original object
/ / shallow copy
const obj1 = {
name : 'init'.arr : [1[2.3].4]};const obj3=shallowClone(obj1) // a shallow copy method
obj3.name = "update";
obj3.arr[1] = [5.6.7];// Old and new objects still share the same memory
console.log('obj1',obj1) // obj1 { name: 'init', arr: [ 1, [ 5, 6, 7 ], 4 ] }
console.log('obj3',obj3) // obj3 { name: 'update', arr: [ 1, [ 5, 6, 7 ], 4 ] }
Copy the code
But deep copy will create another identical object, the new object and the original object do not share memory, modify the new object will not change to the original object
/ / copy
const obj1 = {
name : 'init'.arr : [1[2.3].4]};const obj4=deepClone(obj1) // a deep-copy method
obj4.name = "update";
obj4.arr[1] = [5.6.7];// The new object does not share memory with the original object
console.log('obj1',obj1) // obj1 { name: 'init', arr: [ 1, [ 2, 3 ], 4 ] }
console.log('obj4',obj4) // obj4 { name: 'update', arr: [ 1, [ 5, 6, 7 ], 4 ] }
Copy the code
summary
If the copy type is reference type:
-
A shallow copy is a copy of a layer. If the property is an object, the shallow copy is a copy. The two objects point to the same address
-
Deep copy is a deep recursive copy. If the property is an object, the deep copy is a new stack, and the two objects point to different addresses
Interviewer: What are anti-shake and throttling? What’s the difference? How to do that?
What is it
Essentially a means of optimizing code for high frequency execution
For example, when events such as resize, Scroll, Keypress, and Mousemove of the browser are triggered, the callback function bound to the event is constantly invoked, which greatly wastes resources and reduces front-end performance
To optimize the experience, we need to limit the number of calls to these events, which we can throttle and debounce to reduce
define
- Throttling: runs only once within n seconds. If it is triggered repeatedly within N seconds, it takes effect only once
- Anti-shake: this event will be executed after n seconds. If it is triggered repeatedly within N seconds, the timer will be reset
A classic metaphor:
Imagine the elevator under your building every day. The elevator completes a delivery, analogous to the execution and response of a function
Assume that the elevator has two running policies, Debounce and Throttle, with timeouts set to 15 seconds regardless of capacity constraints
The first person in the elevator comes in, 15 seconds after the exact delivery, this is throttling
Wait 15 seconds after the first person enters the elevator. If someone else comes in during the process, wait for 15 seconds to reset the timer until shipping begins after 15 seconds. This is anti-shake
Code implementation
The throttle
Throttling can be done using a timestamp and timer notation
With timestamps, the event is executed immediately and there is no way to execute again after the trigger is stopped
function throttled1(fn, delay = 500) {
let oldtime = Date.now()
return function (. args) {
let newtime = Date.now()
if (newtime - oldtime >= delay) {
fn.apply(null, args)
oldtime = Date.now()
}
}
}
Copy the code
If the timer is used, the timer is executed for the first time after the delay is milliseconds, and the timer is executed again after the event is stopped for the second time
function throttled2(fn, delay = 500) {
let timer = null
return function (. args) {
if(! timer) { timer =setTimeout(() = > {
fn.apply(this, args)
timer = null}, delay); }}}Copy the code
You can combine the features of timestamp writing with those of timer writing to achieve a more precise throttling. To achieve the following
function throttled(fn, delay) {
let timer = null
let starttime = Date.now()
return function () {
let curTime = Date.now() // The current time
let remaining = delay - (curTime - starttime) // How much extra time is left since last time
let context = this
let args = arguments
clearTimeout(timer)
if (remaining <= 0) {
fn.apply(context, args)
starttime = Date.now()
} else {
timer = setTimeout(fn, remaining); }}}Copy the code
Image stabilization
Simple version of the implementation
function debounce(func, wait) {
let timeout;
return function () {
let context = this; // Save this point
let args = arguments; // Get the event object
clearTimeout(timeout)
timeout = setTimeout(function(){ func.apply(context, args) }, wait); }}Copy the code
If you need to perform the anti-shake function immediately, you can add the third parameter for judgment. The implementation is as follows:
function debounce(func, wait, immediate) {
let timeout;
return function () {
let context = this;
let args = arguments;
if (timeout) clearTimeout(timeout); // timeout is not null
if (immediate) {
letcallNow = ! timeout;// It will be executed immediately for the first time, and will be triggered again only after the event is executed
timeout = setTimeout(function () {
timeout = null;
}, wait)
if (callNow) {
func.apply(context, args)
}
}
else {
timeout = setTimeout(function () { func.apply(context, args) }, wait); }}}Copy the code
Second, the difference between
Similarities:
- Both can be used
setTimeout
implementation - The goal is to reduce the frequency of callbacks. Saving computing Resources
Difference:
- Function stabilization, at the end of a sequence of operations, to handle the callback, using
clearTimeout
andsetTimeout
The implementation. Function throttling, used to improve performance during high frequency events that are executed only once at a time during a continuous operation - Function chattering focuses on events that are triggered consecuentially over a period of time and are executed only once last, whereas function throttling is executed only once over a period of time
For example, both sets the time frequency to 500ms, triggers functions frequently, throttles frequently, and executes every 500ms over a 2-second period. Anti-shake, no matter how many times the method is deployed, after 2s, it will only be executed once
As shown below:
Three, application scenarios
Shudder in successive events that only need to trigger a callback scenario are:
- The search box searches for input. The user only needs to type one last time before sending the request
- Mobile phone number, email verification input detection
- The window size
resize
. Just after the window adjustment is complete, calculate the window size. Prevent repeated rendering.
Throttling performs a callback at intervals in the following scenarios:
- Scroll to load, load more or roll bottom to listen
- Search box, search associative functions
Interviewer: What are some common DOM operations?
A, DOM
The Document Object Model (DOM) is a programming interface for HTML and XML documents
It provides a structured representation of a document and defines a way for that structure to be accessed from within the program to change the structure, style, and content of the document
Any HTML or XML document can be represented in the DOM as a hierarchy of nodes
Nodes are divided into many types, each of which corresponds to different information and/or tags in the document and has its own different features, data, and methods, and has some relationship with other types, as shown below:
<html>
<head>
<title>Page</title>
</head>
<body>
<p>Hello World! </p ></body>
</html>
Copy the code
Just as DOM atoms contain subatomic particles, there are many types of DOM nodes that contain other types of nodes. Let’s take a look at three of them:
<div>
<p title="title">
content
</p >
</div>
Copy the code
In the above structure, div and P are element nodes, Content is text node, and title is attribute node
Second, the operating
In daily front-end development, we all need DOM manipulation
In the past, we used Jquery, Zepto and other libraries to manipulate the DOM. Later, with the advent of vue, Angular, React and other frameworks, we controlled the DOM by manipulating data (mostly), and less and less by manipulating the DOM directly
But that doesn’t mean native operations aren’t important. Instead, DOM manipulation helps us understand the underlying content of the framework
The following is an analysis of common DOM operations, which are mainly divided into:
- Create a node
- Query node
- Update the node
- Add a node
- Remove nodes
Create a node
createElement
Creates a new element that takes a parameter, the name of the tag to create the element
const divEl = document.createElement("div");
Copy the code
createTextNode
Create a text node
const textEl = document.createTextNode("content");
Copy the code
createDocumentFragment
To create a document shard, which represents a lightweight document that is primarily used to store temporary nodes, and then adds the contents of the document shard to the DOM at once
const fragment = document.createDocumentFragment();
Copy the code
When a DocumentFragment node is requested to be inserted into the document tree, instead of the DocumentFragment itself, all of its descendants are inserted
createAttribute
Create a property node, which can be a custom property
const dataAttribute = document.createAttribute('custom');
consle.log(dataAttribute);
Copy the code
Access to the node
querySelector
Pass in any valid CSS selector to select a single DOM element (first) :
document.querySelector('.element')
document.querySelector('#element')
document.querySelector('div')
document.querySelector('[name="username"]')
document.querySelector('div + p > span')
Copy the code
Returns null if there is no specified element on the page
querySelectorAll
Returns a list of all matching Element nodes in the node tree, or an empty node list if none matches
const notLive = document.querySelectorAll("p");
Copy the code
It is important to note that this method returns a static instance of NodeList, which is a static “snapshot” rather than a “live” query
There are other ways to get DOM elements, but I won’t cover them all
document.getElementById('ID attribute value'); Returns a reference to the object with the specified IDdocument.getElementsByClassName('Class attribute value'); Returns owning the specifiedclassObject collection ofdocument.getElementsByTagName(' tag name '); Returns a collection of objects with the specified label namedocument.getElementsByName('Name attribute value'); Returns the combination of objects with the specified namedocument/element.querySelector('CSS selector '); Only the first matched element is returneddocument/element.querySelectorAll('CSS selector '); Returns all matched elementsdocument.documentElement; Gets the HTML tag from the pagedocument.body; Gets the BODY tag on the pagedocument.all[' ']; Gets the object collection type of all element nodes in the pageCopy the code
In addition, each DOM element also has parentNode, childNodes, firstChild, lastChild, nextSibling, and previousSibling attributes, as shown below
Update the node
innerHTML
Not only can you modify the text content of a DOM node, you can also modify the subtree inside the DOM node directly through HTML fragments
...
var p = document.getElementById('p');
// Set the text to ABC:
p.innerHTML = 'ABC'; // <p id="p">ABC</p >
/ / set the HTML:
p.innerHTML = 'ABC <span style="color:red">RED</span> XYZ';
// ... The internal structure of
has been modified
Copy the code
The innerText, textContent
Automatically htML-encode strings, ensuring that no HTML tags can be set
<p id="p-id">... </p > var p = document.getElementById('p-id'); / / set the text: p.i nnerText = '< script > alert (" Hi ") < / script >'; <p id="p-id">< // <p id="p-id">< script> alert("Hi")< /script> </p >Copy the code
The difference is that when reading an attribute, innerText does not return the text of the hidden element, whereas textContent returns all of the text
style
The STYLE property of a DOM node corresponds to all CSS and can be obtained or set directly. Encountered – Needs to be converted to a hump name
...
const p = document.getElementById('p-id');
/ / set the CSS:
p.style.color = '#ff0000';
p.style.fontSize = '20px'; // Name the hump
p.style.paddingTop = '2em';
Copy the code
Add a node
innerHTML
If the DOM node is empty, for example,
, then you can modify the content of the DOM node by using innerHTML = ‘< SPAN >child’, equivalent to adding a new DOM node
If the DOM node is not empty, you can’t do this because innerHTML simply replaces all of the original child nodes
appendChild
Adds a child node to the last child node of the parent node
For example
<! - HTML structure - > JavaScript < p id = "JavaScript" > < / p > < div id = "list" > < p id = "Java" > Java < / p > < p id = "python" > python < / p > < p id="scheme">Scheme</p > </div>Copy the code
Add a p element
const js = document.getElementById('js')
js.innerHTML = "JavaScript"
const list = document.getElementById('list');
list.appendChild(js);
Copy the code
Now the HTML structure becomes the following
<! - HTML structure -- -- > < div id = "list" > < p id = "Java" > Java < / p > < p id = "python" > python < / p > < p id = "scheme" > scheme < / p > < p id="js">JavaScript</p > <! </div>Copy the code
In the above code, we add the DOM element after obtaining it. The JS node already exists in the current document tree. Therefore, the node will be deleted from its original position and then inserted into the new position
If new nodes are added dynamically, a new node is created and then inserted into the specified location
const list = document.getElementById('list'),
const haskell = document.createElement('p');
haskell.id = 'haskell';
haskell.innerText = 'Haskell';
list.appendChild(haskell);
Copy the code
insertBefore
Insert the child node at the specified position as follows:
parentElement.insertBefore(newElement, referenceElement)
Copy the code
Child nodes are inserted before the referenceElement
setAttribute
Adds an attribute node to the specified element, changing the value of the attribute if the element already exists
const div = document.getElementById('id')
div.setAttribute('class'.'white');// The first parameter attribute name, the second parameter attribute value.
Copy the code
Remove nodes
To remove a node, first get the node itself and its parent, and then call removeChild on the parent node to remove itself
// Get the node to be deleted:
const self = document.getElementById('to-be-removed');
// Get the parent node:
const parent = self.parentElement;
/ / remove:
const removed = parent.removeChild(self);
removed === self; // true
Copy the code
The deleted node is no longer in the document tree, but it is still in memory and can be added to another location at any time
A link to the
Developer.mozilla.org/zh-CN/docs/…
Interviewer: Tell me about your understanding of the cycle of events
What is it
First of all, JavaScript is a single-threaded language, which means you can only do one thing at a time, but that doesn’t mean that single-threaded is blocking, and the way to achieve single-threaded non-blocking is event loops
In JavaScript, all tasks can be categorized
-
Synchronization task: The synchronization task is executed immediately. The synchronization task is usually directly executed in the main thread
-
Asynchronous tasks: Tasks that are executed asynchronously, such as Ajax web requests, setTimeout timing functions, etc
The operation flow of synchronous and asynchronous tasks is as follows:
As can be seen from the above, synchronous tasks enter the main thread, that is, the main execution stack, while asynchronous tasks enter the task queue. When the execution of tasks in the main thread is empty, the corresponding tasks will be read from the task queue and pushed to the main thread for execution. The repetition of the above process creates a cycle of events
Macro task and micro task
If dividing tasks into synchronous and asynchronous tasks is not accurate, here is an example:
console.log(1)
setTimeout(() = >{
console.log(2)},0)
new Promise((resolve, reject) = >{
console.log('new Promise')
resolve()
}).then(() = >{
console.log('then')})console.log(3)
Copy the code
If we follow the above flowchart to analyze the code, we get the following execution steps:
console.log(1)
, the synchronization task is executed in the main threadsetTimeout()
, asynchronous task, intoEvent Table
, 0 milliseconds laterconsole.log(2)
The callback pushEvent Queue
中new Promise
, synchronization task, main thread directly executed.then
, asynchronous task, intoEvent Table
console.log(3)
, synchronization task, main thread execution
So the result should be 1 => ‘new Promise’ => 3 => 2 => ‘then’
But the actual result is: 1=>’new Promise’=> 3 =>’ then’ => 2
The reason for the divergence is the order in which asynchronous tasks are executed. The event queue is actually a “first in, first out” data structure, and the first events are read by the main thread first
In this example, the setTimeout callback event is queued first and should be executed before.then, but the result is the opposite
The reason is that asynchronous tasks can be subdivided into microtasks and macro tasks
Micro tasks
A function that needs to be executed asynchronously, after the execution of the main function and before the completion of the current macro task
Common microtasks include:
-
Promise.then
-
MutaionObserver
-
Object. Observe (obsolete; Proxy object substitution)
-
Process. NextTick (Node. Js)
Macro task
The time granularity of macro tasks is relatively large, and the time interval of execution cannot be precisely controlled, which does not meet the requirements of high real-time performance
Common macro tasks are:
- Script (can be understood as the outer synchronization code)
- setTimeout/setInterval
- The UI rendering/UI events
- PostMessage, MessageChannel
- SetImmediate, I/O (Node.js)
At this point, the relationship between event loops, macro tasks, and micro tasks is shown in the figure below
According to this process, its execution mechanism is:
- Execute a macro task, and if it encounters a microtask, place it in the event queue of the microtask
- After the current macro task is executed, the event queue of the microtask is viewed and all the microtasks in it are executed in sequence
Let’s go back to the problem above
console.log(1)
setTimeout(() = >{
console.log(2)},0)
new Promise((resolve, reject) = >{
console.log('new Promise')
resolve()
}).then(() = >{
console.log('then')})console.log(3)
Copy the code
The process is as follows
// If console.log(1) is encountered, print 1
// When a timer is encountered, it belongs to a new macro task, which is reserved for later execution
// When you encounter a new Promise, this is implemented directly, print 'new Promise'
//. Then belongs to the microtask, which is put into the microtask queue and executed later
// If console.log(3) is encountered, print 3 directly
// Now go to the list of microtasks to see if there are any microtasks, find the. Then callback, execute it, print 'then'
// When the first macro task is completed, the next macro task is executed. There is only one timer macro task left
Copy the code
Async and await
‘async’ means’ async ‘and’ await ‘means’ async wait’. So async is used to declare an asynchronous method and await is used to wait for an asynchronous method to execute
async
The async function returns a Promise object. The following two methods are equivalent
function f() {
return Promise.resolve('TEST');
}
// asyncF is equivalent to f!
async function asyncF() {
return 'TEST';
}
Copy the code
await
Normally, the await command is followed by a Promise object that returns the result of that object. If it is not a Promise object, the corresponding value is returned
async function f(){
/ / is equivalent to
// return 123
return await 123
}
f().then(v= > console.log(v)) / / 123
Copy the code
“Await” blocks the following code regardless of what is followed by “await”
async function fn1 (){
console.log(1)
await fn2()
console.log(2) / / blocking
}
async function fn2 (){
console.log('fn2')
}
fn1()
console.log(3)
Copy the code
In the example above, await will block the following code (i.e. join microtask queue), execute the synchronous code outside async, finish the synchronous code, return to async function, and execute the blocked code
So the above output results are: 1, fn2, 3,2
Iv. Process analysis
From the above, we have a general idea of the order in which JavaScript executes various scenarios
Here’s the code:
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')}async function async2() {
console.log('async2')}console.log('script start')
setTimeout(function () {
console.log('settimeout')
})
async1()
new Promise(function (resolve) {
console.log('promise1')
resolve()
}).then(function () {
console.log('promise2')})console.log('script end')
Copy the code
Analysis process:
- Execute the entire code, encountered
console.log('script start')
Print the result directly and output itscript start
- Timer encountered, it is a macro task, put first do not execute
- encounter
async1()
, the implementation ofasync1
Function, print firstasync1 start
, the following encounterawait
How to do? To perform firstasync2
To printasync2
, then block the following code (join the list of microtasks) and jump out to execute the synchronized code - Jump to the
new Promise
In this case, just execute, printpromise1
, the following encounter.then()
, it is a microtask, placed on the microtask list waiting to be executed - The last line is printed directly
script end
, now that the synchronization code is complete, start the microtask, i.eawait
The following code, printasync1 end
- Move on to the next microtask, execute
then
The callback to printpromise2
- Now that I’ve done everything for the last macro task, I’m going to do the next macro task, which is timer, print
settimeout
So the final result is: script start, async1 start, Async2, promise1, script end, AsynC1 end, promise2, setTimeout