JavaScript (JS) is a programming language that is used for dynamic scripting of web pages, usually on the client-side, but also on the server-side, via packages like Node.js.
Today, send an article about Js basic knowledge points, for more new directions. There will always be someone in front of you to explore the way for you, the road ahead, you are not alone ~
Let’s start with a directory structure
├ ─2, ├ ─ unteached, unteached, unteached, unteached, unteached, unteached, unteached, unteached, unteached, unteached, unteached, unteached, unteached How is it formed? ─ ─ ─ 4, how to understand both synchronous and asynchronous │ └ ─ ─ ─ synchronous vs asynchronous │ └ ─ ─ ─ asynchronous and single thread │ └ ─ ─ ─ the front-end asynchronous scenario description ─ ─ ─ 5, simple describe the understanding of the ES6 / ES7 │ └ ─ ─ ─ deconstruction assignment │ └ ─ ─ ─ the arrow function │ └ ─ ─ ─ Promise ├ ─ └─ Set and Map data structuresCopy the code
1. Variable declarations
1-1. Classification and judgment of JavaScript data types
In JavaScript, there are seven basic types:
- String,
- Number,
- Bigint,
- Boolean.
- Null,
- Undefined,
- Symbol (new in ECMAScript 2016).
String, number, Boolean, undefined, Null and symbol are six primitive types.
It is worth noting that the primitive type does not contain Object.
What methods are used for type determination?
1, the typeof
Typeof XXX returns values of the following types: undefined Boolean Number String object Function symbol
Such as:
console.log(typeof 42);
// expected output: "number"
console.log(typeof 'blubber');
// expected output: "string"
console.log(typeof true);
// expected output: "boolean"
console.log(typeof declaredButUndefinedVariable);
// expected output: "undefined";
Copy the code
typeof null
As a result,object
,JavaScript
Ever since we were born, becausenull
Represents a null pointer (0x00 on most platforms), so,null
The type tag of the0
.typeof null
And therefore return"object"
.typeof [1, 2]
As a result,object
The result is nonearray
This item, the reference type exceptfunction
Everything else isobject
typeof Symbol()
用typeof
To obtainsymbol
The value of type issymbol
,Symbol
Instances are unique and immutable — this is a new wrinkle in ES6.
2, instanceof
Used for instance and constructor correspondence. For example, if a variable is an Array, use [1, 2] instanceof Array instead of typeof. Return true. Because, [1, 2] is an Array, its constructor is Array. In the same way:
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
var auto = new Car('Honda'.'Accord'.1998);
console.log(auto instanceof Car);
// expected output: true
console.log([1.2] instanceof Array);
// expected output: true
Copy the code
1-2. Reference types and value types
In addition to primitive types, JS also has reference types. The above mentioned Typeof recognizes only object and function as reference types. The other types are value types.
Value type variables include Boolean, String, Number, Undefined, Null, and reference types include all Object types. Such as Date, Array, Function, etc. In terms of parameter passing, value types are passed by value and reference types are passed by share.
/ / value types
var a = 1;
var b = a;
b = 3
console.log(a) / / 1
console.log(b) / / 3
// both a and B are value types.
Copy the code
// Reference type
var a = {x: 10.y: 20}
var b = a
b.x = 100
b.y = 200
console.log(a) // {x: 100, y: 200}
console.log(b) // {x: 100, y: 200}
Copy the code
Both a and B are reference types. After b = a, change the value of b’s attribute and change that of A. Because a and B are reference types, they refer to the same memory address, that is, they refer to the same value, so when B modifies the attribute, the value of A changes accordingly.
2. Prototype and Prototype Chain (Inheritance)
JavaScript is often described as a prototype-based language — each object has a prototype object from which the object is a template and inherits methods and properties. A stereotype object may also have a stereotype from which it inherits methods and properties, layer by layer, and so on. This relationship, often referred to as the Prototype chain, explains why one object has properties and methods defined in other objects.
Note: It is important to understand the difference between an Object’s prototype (which can be obtained via object.getProtoTypeof (obj) or the deprecated __proto__ attribute) and the constructor’s prototype attribute. The former is a property that exists on every instance, and the latter is a property of the constructor. That is, object.getProtoTypeof (new Foobar()) and foobar.prototype refer to the same Object.
In javascript, functions can have attributes. Each function has a special property called prototype, as shown below. Note that the code below is a separate piece (it is safe in the absence of other code on the page). For the best learning experience, open a Console (Ctrl+Shift+I in Chrome and Firefox), switch to the “Console” TAB, copy and paste the JavaScript code below, and press Enter to run it.
function doSomething(){}
console.log( doSomething.prototype );
// No matter how you declare functions, functions in javascript always have a default stereotype property
var doSomething = function(){};
console.log( doSomething.prototype );
Copy the code
As you can see above, the doSomething function has a default stereotype property, which is rendered on the console. After running this code, an object like this should appear on the console.
{
constructor: ƒ doSomething (),__proto__: {
constructor: ƒ Object(),
hasOwnProperty: ƒ `hasOwnProperty`(),
isPrototypeOf: ƒ `isPrototypeOf`(),
propertyIsEnumerable: ƒ `propertyIsEnumerable`(),
toLocaleString: ƒ `toLocaleString`(),
toString: ƒ `toString`(),
valueOf: ƒ `valueOf`()}}Copy the code
Now we can add some properties to the doSomething prototype, as follows:
function doSomething(){}
doSomething.prototype.foo = "bar";
console.log( doSomething.prototype );
Copy the code
Output:
{
foo: "bar".constructor: ƒ doSomething (),__proto__: {
constructor: ƒ Object(),
hasOwnProperty: ƒ `hasOwnProperty`(),
isPrototypeOf: ƒ `isPrototypeOf`(),
propertyIsEnumerable: ƒ `propertyIsEnumerable`(),
toLocaleString: ƒ `toLocaleString`(),
toString: ƒ `toString`(),
valueOf: ƒ `valueOf`()}}Copy the code
We can then use the new operator to create an instance of doSomething based on our current prototype.
function doSomething(){}
doSomething.prototype.foo = "bar"; // add a property onto the prototype
var doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value"; // add a property onto the object
console.log( doSomeInstancing );
Copy the code
Output:
{
prop: "some value".__proto__: {
foo: "bar".constructor: ƒ doSomething (),__proto__: {
constructor: ƒ Object(),
hasOwnProperty: ƒ `hasOwnProperty`(),
isPrototypeOf: ƒ `isPrototypeOf`(),
propertyIsEnumerable: ƒ `propertyIsEnumerable`(),
toLocaleString: ƒ `toLocaleString`(),
toString: ƒ `toString`(),
valueOf: ƒ `valueOf`()}}}Copy the code
As you can see above, doSomeInstancing’s __proto__ property is dosomething.prototype. But what good is that? Well, when you visit a property on doSomeInstancing, the browser first looks to see if doSomeInstancing has that property. If doSomeInstancing doesn’t have this property, then the browser will look for it in doSomeInstancing’s __proto__ (that is, dosome.prototype). If doSomeInstancing __proto__ has this property, then the property on doSomeInstancing __proto__ will be used. Otherwise, if doSomeInstancing’s __proto__ doesn’t have this property, the browser will go to find doSomeInstancing’s __proto__ to see if it does. By default, the __proto__ of the prototype attribute for all functions is window.object.prototype. So doSomeInstancing __proto__ (aka doSomething. Prototype’s __proto__ (aka object.prototype)) will be looked up to see if it has this property. If you don’t find the property in it, then look at doSomeInstancing in __proto__’s __proto__. There’s a problem with this, however: doSomeInstancing’s __proto__ __proto__ doesn’t exist. Finally, all __proto__ on the prototype chain is found, and the attribute is not found on all __proto__ declared by the browser, and it is concluded that the attribute is undefined.
function doSomething(){}
doSomething.prototype.foo = "bar";
var doSomeInstancing = new doSomething();
doSomeInstancing.prop = "some value";
console.log("doSomeInstancing.prop: " + doSomeInstancing.prop);
console.log("doSomeInstancing.foo: " + doSomeInstancing.foo);
console.log("doSomething.prop: " + doSomething.prop);
console.log("doSomething.foo: " + doSomething.foo);
console.log("doSomething.prototype.prop: " + doSomething.prototype.prop);
console.log("doSomething.prototype.foo: " + doSomething.prototype.foo);
Copy the code
Output:
doSomeInstancing.prop: some value
doSomeInstancing.foo: bar
doSomething.prop: undefined
doSomething.foo: undefined
doSomething.prototype.prop: undefined
doSomething.prototype.foo: bar
Copy the code
It’s too much to look at. Don’t worry. Look at this:
- All reference types (arrays, objects, functions) have object properties and can be freely extended.
null
Except)- For all reference types (arrays, objects, functions), there is one
__proto__
Property, the property value is a normal object- All of the functions, there’s one
prototype
Property, the property value is also an ordinary object- All reference types (arrays, objects, functions),
__proto__
Property that points to its constructorprototype
Attribute values
// Point 1: Freely extend attributes
var obj = {}; obj.a = 100;
var arr = []; arr.a = 100;
function fn () {}
fn.a = 100;
// __proto__
console.log(obj.__proto__);
console.log(arr.__proto__);
console.log(fn.__proto__);
// The function has prototype
console.log(fn.prototype)
The __proto__ attribute value of a reference type points to the prototype attribute value of its constructor
console.log(obj.__proto__ === Object.prototype)
Copy the code
2-1. Prototype and prototype chain
The prototype
// constructor
function Foo(name, age) {
this.name = name
}
Foo.prototype.alertName = function () {
alert(this.name)
}
// Create an example
var f = new Foo('zhangsan')
f.printName = function () {
console.log(this.name)
}
/ / test
f.printName()
f.alertName()
Copy the code
Well, printName makes sense, but what happens when alertName is executed? One more important thing to remember here is that when trying to get a property of an object, if the object doesn’t have the property itself, it looks in its __proto__ (its constructor’s prototype), So f.lertName will find foo.prototype.alertName.
So how do you determine if this property is a property of the object itself? The common place to use hasOwnProperty is when iterating over an object.
var item
for (item in f) {
// Advanced browsers have already masked attributes from prototypes in for in, but it is recommended that you add this judgment to ensure normal output
if (f.hasOwnProperty(item)) {
console.log(item)
}
}
Copy the code
Prototype chain
Continuing with the above example, what happens if f.tostring () is executed?
f.printName()
Copy the code
Because f itself doesn’t have toString(), and f.__proto__ (foo.prototype) doesn’t have toString. The problem comes back to the previous statement that when trying to get an attribute of an object, if the object doesn’t have the attribute itself, it will look in its __proto__ (its constructor’s prototype).
If you don’t find toString in f.proto, go to F.proto, because F.proto is a common object.
f.__proto__
即Foo.prototype
, failed to findtoString
Keep looking upf.__proto__.__proto__
即Foo.prototype.__proto__
。Foo.prototype
It’s just a normal object, soFoo.prototype.__proto__
isObject.prototype
It can be found heretoString
- so
f.toString
And finally we get toObject.prototype.toString
If you go all the way up, you’ll find a chain, so it’s called a prototype chain. If the topmost layer is not found, it fails and returns undefined. Object.prototype.__proto__ === null The prototype chain is not infinite; the prototype chain eventually points to null.
Reference article: Simple and crude understanding of JS prototype chain – JS object-oriented programming
Please click on wechat to read the original article for a better view.
3. Scopes and closures
Scopes and closures are the most likely topics to be examined in a front-end interview
3-1. Scope
A scope is an independent domain that keeps variables from leaking and being exposed.
There are only two types of scope for variables: global variables and local variables.
Global scope:
The variables defined by the outermost function have global scope, that is, they are accessible to any inner function
var outerVar = "outer";
function fn(){
console.log(outerVar);
}
fn(); // result:outer
Copy the code
Local scope:
In contrast to global scopes, local scopes are generally accessible only within fixed code fragments and not outside of functions, most commonly inside functions
function fn(){
var innerVar = "inner";
}
fn();
console.log(innerVar); // ReferenceError: innerVar is not defined
Copy the code
That’s why jQuery, Zepto, etc., all the code is placed in (function(){…. }) (). Because all variables placed inside will not be leaked and exposed, will not pollute the outside, will not affect other libraries or JS scripts. This is a representation of the scope of the function.
Note: ES6 is starting to add block-level scope, using let to define variables as follows:
if (true) {
let name = 'Tom'
}
console.log(name) // Error because let defines name in if block-level scope
Copy the code
In this code, console.log(a) attempts to get variable A, but no a is defined in the current scope, and looks up level by level until it finds the global scope. This layer by layer relationship is the scope chain.
var a = 5
function fn() {
var b = 10
console.log(a)
console.log(b)
}
fn()
Copy the code
3-2 What is a closure and how is it formed
So what is a closure? There are many opinions, and the most frequent ones are the following two:
- Functions cover functions.
- The technique of obtaining variables from outside a function.
function F1() {
var a = 100
return function () {
console.log(a)
}
}
var f1 = F1()
var a = 200
f1()
Copy the code
Closures have two main applications:
- Function as the return value, as in the example above
- Functions are passed as arguments, as shown in the following example
function F1() {
var a = 100
return function () {
console.log(a)
}
}
function F2(f1) {
var a = 200
console.log(f1())
}
var f1 = F1()
F2(f1)
Copy the code
About this object
var name = "The Window";
var object = {
name : "My Object".getNameFunc : function(){
return function(){
return this.name; }; }}; alert(object.getNameFunc()());// result:The Window
Copy the code
The this object is bound at runtime based on the function’s execution environment: in a global function, this equals window, and when the function is called as an object, this equals that object. However, anonymous functions are global, so this object will always point to window.
4. How to understand synchronous and asynchronous
4-1. Synchronous vs asynchronous
First print 100, then print 200 after 1 second, then print 300. But the actual operation is nothing like that.
console.log(100)
setTimeout(function () {
console.log(200)},1000)
console.log(300)
Copy the code
Compare the following program. Print 100, pop up 200 (for user confirmation), and print 300. This operation results in the desired requirements.
console.log(100)
alert(200) // After 1 second, click Ok
console.log(300)
Copy the code
What’s the difference between these two? The middle step in the first example does not block subsequent programs at all, while the second example does. This behavior is called asynchronous (the latter is called synchronous) and does not block subsequent programs.
4-2, Asynchronous and single-threaded
setTimeout(function(){
a = false;
}, 100)
while(a){
console.log('While executes')}Copy the code
Since JS is single-threaded and can only do one thing at a time, after entering the while loop, there is no “time” (thread) to run the timer, so the code runs in an infinite loop!
4-3 Front-end asynchronous scenario description
- Scheduled task:
setTimeout
.setInterval
- Bind events:
addEventListener
(click
Etc.) - Network request:
ajax
andimg
Dynamic loading
5, briefly describe the understanding of ES6/ES7
5-1. Deconstruct assignment
ES6 allows you to extract values from arrays and objects and assign values to variables in a pattern called Destructuring.
Previously, to assign a value to a variable, you had to specify a value directly.
let a = 1;
let b = 2;
let c = 3;
Copy the code
ES6 allows you to write it like this.
let [a, b, c] = [1.2.3];
Copy the code
The assignment code is greatly reduced. There is no need to declare the definition and assignment of variables a,b, and c respectively, just take variables a,b, and c as elements of an array, and then assign the array [1,2,3] to the array [a,b,c]. Variables a,b, and c can get the corresponding values respectively.
1. Structure assignments can be nested
let [ a,b,[ c1,c2 ] ] = [ 1.2[3.1.3.2]].console.log(c1);// c1 has a value of 3.1
console.log(c2);// c2 has a value of 3.2
Copy the code
2. Incomplete deconstruction
let [a, b, c] = [1.2];
console.log(a);// the value of a is 1
console.log(b);// the value of b is 2
Copy the code
3. The value of the variable is equal to undefined if the deconstruction fails.
let [a,b,c] = [1.2];
console.log(a);// the value of a is 1
console.log(b);// the value of b is 2
console.log(c);// Result: c is undefined
Copy the code
4. Destruct assignment allows specifying default values
let [foo = true] = [];
foo // true
let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a'.undefined]; // x='a', y='b'
Copy the code
Note that ES6 internally uses the strict equality operator (===) to determine whether a position has a value. Therefore, the default value is valid only if an array member is strictly equal to undefined.
Object destructuring assignment
var { a,b,c } = {"a":1."c":3."b":2};
console.log(a);// Result: A = 1
console.log(b);// result: b is 2
console.log(c);// Result: c = 3
Copy the code
Destruct assignment of a string
var [a,b,c,d,e,f] = "I am a bird.";
console.log(a);/ / I
console.log(b);/ / is
console.log(c);/ / a
console.log(d);/ / only
console.log(e);/ / small
console.log(f);/ / bird
Copy the code
Deconstruct the purpose of assignment
Swap values of variables
var x = 1;
var y = 2;
[x,y] = [y,x];
Copy the code
Extract multiple values returned by the function
function demo(){
return {"name": "Zhang"."age": 21}}var {name,age} = demo();
console.log(name);// Result: Zhang SAN
console.log(age);// Result: 21
Copy the code
Define function parameters
function demo({a,b,c}){
console.log("Name:"+ a);
console.log("Height:"+ b);
console.log("Weight:"+ c);
}
demo({a:"Tang three".b:"1.72 m".c:"50kg".d:"8000"});
/* It is very easy to extract the desired parameters from the JSON object. For example, in our case, we only need to get the parameters a, b, and c, and do not need other parameters, such as d or more. * /
Copy the code
Extract JSON data
let jsonData = {
id: 42.status: "OK".data: [867.5309]};let { id, status, data: number } = jsonData;
console.log(id, status, number);
// 42, "OK", [867, 5309]
Copy the code
Fifth, the input module of the specified method
When a module is loaded, it is often necessary to specify which methods to input. Deconstructing assignment makes the input statement very clear.
const { SourceMapConsumer, SourceNode } = require("source-map");
Copy the code
5-2. Grammar of Module
Historically, JavaScript has not had a module system that allows you to break up a large program into small interdependent files and put them together in a simple way. Other languages have this functionality, such as Ruby’s require, Python’s import, and even CSS has @import, but JavaScript has no support for any of this, which is a huge barrier to developing large, complex projects.
/ / CommonJS module
let { stat, exists, readFile } = require('fs');
/ / is equivalent to
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;
Copy the code
The above code essentially loads the FS module as a whole (that is, all the methods that load FS), generates an object (_fs), and then reads three methods from that object. This type of loading is called “runtime loading” because the object is only available at runtime, making it completely impossible to do “static optimization” at compile time.
Export: As a module, it can optionally expose (provide) its own properties and methods to other modules for use by other modules.
Import Import: A module can Import properties or methods provided by other modules as needed for its own module to use.
Modular implementation
/ / module - - B.j s file
// Export variable name
export var name = "Modular";
Copy the code
Module B uses the keyword export to expose an attribute: the value of name is the string “modular”. A key word, a code to achieve, is not very simple.
/ / module - - a. s file
// Import the attribute name of module B
import { name } from "./module-B.js";
console.log(name)
// Prints the result: modularization
Copy the code
Module A imports the name attribute of module B using the keyword import and assigns the value to the variable name. The keyword from is used to specify which module you want to import. In this case, we specify module-b.js file, which is module B above. “Modularity” is the exposed property of module B.
5-3. Arrow functions
This in the arrow function refers to this at definition, not this at execution.
// Define an object
var obj = {
x:100./ / property x
show(){
// Delay 500 ms, output x value
setTimeout(
// Difference: arrow function() = > {console.log(this.x)},
500); }}; obj.show();// Print the result: 100
Copy the code
When we define the show() method of obj, we write this.x in the arrow function, where this refers to obj, so this.x refers to obj.x. When show() is called, this still refers to the object to which it was defined, which is obj, so it prints: 100.
5-4. Promise objects
Promise is a solution to asynchronous programming that makes more sense and is more powerful than traditional solutions — callback functions and events. It was first proposed and implemented by the community, and ES6 has written it into the language standard, unifying usage, and providing Promise objects natively.
ES6 specifies that a Promise object is a constructor that generates a Promise instance.
Promise objects have three states:
pending
: indicates the initial state when a Promise instance is created;fulfilled
If the: resolve method is invoked, the operation succeeds.rejected
When the: reject method is invoked, the operation fails.
This state can only be reversed from the initialization -> success or the initialization -> failure, and can not be reversed between the success pity and the failure rejected.
const pro = new Promise(function(resolve, reject) {
// ... some code
if (/* Asynchronous operation succeeded */){
resolve(value);
} else{ reject(error); }});Copy the code
With Promise creation and state in mind, let’s look at one of the most important instance methods: the then() method.
pro.then(function (res) {
// The handler that operated successfully
},function (error) {
// The handler for the failed operation
});
// Arguments are two functions, the first for the successful operation and the second for the abnormal operation.
Copy the code
The catch () method
pro.catch(function (error) {
// The handler for the failed operation
});
Copy the code
Chained invocation is possible because both the then and catch methods, when called, return a Promise object.
If you haven’t heard of Promise before, you’re probably confused right now. That’s okay. Let’s use an example to connect the dots.
// Create a Promise instance with the new keyword
let pro = new Promise(function(resolve,reject){
// Assume condition is true
let condition = true;
if(condition){
// Call the operation success method
resolve('Operation successful');
// State: pending-> depressing
}else{
// Call the operation exception method
reject('Operation exception');
// Status: Pending -> Rejected}});// Use then to process the operation successfully, catch to process the operation exception
pro.then(function (res) {
// The handler that operated successfully
console.log(res)
}).catch(function (error) {
// The handler for the failed operation
console.log(error)
});
// Console output: Operation successful
Copy the code
The comments for the case above are very detailed, connecting all of the points mentioned above: instance creation, state transitions, and the use of then and catch methods.
Since we set the value of condition to true, the console output after execution is: “Operation succeeded.”
This is the process that Promise uses to handle operation exceptions; But, as we discussed at the beginning of this article, what if there are layers of dependencies between multiple operations?
let pro = new Promise(function(resolve,reject){
if(true) {// Call the operation success method
resolve('Operation successful');
}else{
// Call the operation exception method
reject('Operation exception'); }});// Use then to process the operation successfully, catch to process the operation exception
pro.then(requestA)
.then(requestB)
.then(requestC)
.catch(requestError);
function requestA(){
console.log('Request A successful');
return "Request B, you're next.";
}
function requestB(res){
console.log('Result of previous step:'+res);
console.log('Request B successful');
return "Request C, you're next.";
}
function requestC(res){
console.log('Result of previous step:'+res);
console.log('Request C successful');
}
function requestError(){
console.log('Request failed');
}
// Print the result:
// Request A succeeded
// The result of the previous step: request B, you are next
// Request B succeeds
// The result of the previous step: request C, you are next
// Request C succeeds
Copy the code
In this case, we create an instance and declare four functions, three of which represent request A, request B, and request C. With the THEN method, three request operations no longer need to be nested in layers. We use the then method to bind the three operations in the order in which they are called. Moreover, if request B depends on the result of request A, we can use the return statement to pass the required data as parameters to the next request. In this case, we use the return implementation to pass parameters to the next operation.
A more intuitive diagram
Promise. All () method
Promise.all() method: Accepts an array as parameter, the elements of which are Promise instance objects. When the state of the instance objects in the parameter is fulfilled, promise.all () will return.
// Create instance pro1
let pro1 = new Promise(function(resolve){
setTimeout(function () {
resolve('Instance 1 operation succeeded');
},5000);
});
// Create instance pro2
let pro2 = new Promise(function(resolve){
setTimeout(function () {
resolve('Instance 2 operation succeeded');
},1000);
});
Promise.all([pro1,pro2]).then(function(result){
console.log(result);
});
// Print result: [" Operation succeeded in instance 1 ", "operation succeeded in Instance 2 "]
Copy the code
Promise. Race () method
Another similar method is the promise.race () method: This parameter requirement is the same as the promise.all () method. The difference is that as long as one of the Promise instances in this parameter changes (whether this is a successful pity or an exception rejected), it will return.
// Initialize instance pro1
let pro1 = new Promise(function(resolve){
setTimeout(function () {
resolve('Instance 1 operation succeeded');
},4000);
});
// Initialize instance pro2
let pro2 = new Promise(function(resolve,reject){
setTimeout(function () {
reject('Instance 2 operation failed');
},2000);
});
Promise.race([pro2,pro1]).then(function(result){
console.log(result);
}).catch(function(error){
console.log(error);
});
// Result: The operation in example 2 fails
Copy the code
We have two instances, same instance pro1, different instance pro2, and this time we call reject.
Because the reject method is executed after 2000 ms in pro2, which is earlier than the 4000 ms in Pro1, the final output reads: Instance 2 failed.
That’s all I have to say about Promise objects, including the concept of callback hell; It refers to the excessive use of nested callback functions, which makes debugging and maintenance extremely inconvenient.
Reference: MDN
How do YOU use ES6 Promise objects