How javascript works

  • The Parse module converts JavaScript code into an AST (abstract syntax tree) because the interpreter does not know JavaScript code directly.
    • If the function is not called, it will not be converted to an AST.
    • Parse’s official V8 documentation: v8.dev/blog/scanne…
  • Ignition is an interpreter that converts an AST into a ByteCode
    • At the same time, TurboFan will collect the information required for optimization (such as the type information of function parameters, which can be used for real calculation);
    • If the function is called only once, Ignition will do the interpreted execution ByteCode;
    • Ignition V8: V8. dev/blog/igniti…
  • TurboFan is a compiler that compiles bytecode to machine code that the CPU can execute directly;
    • If a function is called multiple times, it is marked as a hotspot function and is converted by TurboFan into optimized machine code to improve performance.
    • However, machine code is actually restored to ByteCode, because if the type changes during the subsequent execution of the function (for example, sum used to perform number, but later changed to String), the optimized machine code does not handle the operation correctly and is converted to ByteCode in reverse.
    • TurboFan V8: V8.dev /blog/ Turbof…

This is how the JavaScript code is executed, but in fact V8’s memory reclamation is another reason for its power, which I won’t discuss here:

  • Orinoco module, which is responsible for garbage collection, reclaiming unwanted memory in the program;
  • Orinoco’s V8 official document: v8.dev/blog/trash-…

Interpreted and compiled languages

A compiled language is one in which a compiler converts a language that humans can understand (a programming language) into a language that machines can understand before code is run.

Interpreted languages are also languages that humans can understand (programming languages) and also need to be converted into machine-understood languages for execution, but at runtime. Therefore, the interpreter must be installed in the environment before execution; But applications written in compiled languages can run directly after compilation.

Cross domain

Q: What is the relationship between session and cookie? And whether the session obtained by the backend through the name ctx.session. is set by the front-end through localStorage or by the backend through this.ctx.session. Name set?

  • The session is stored on the server. The backend sets the session, and when the front end requests it, it willwithCredentialsSet this to true and the cookie will be sent to the server.
  • When the server executes the session mechanism, it generates the session ID. This id is sent to the client. The client will put this ID in the header of the HTTP request and send it to the server. Therefore, when we completely ban the cookie of the browser, the session on the server will also fail to work properly.
  • Session is a data structure stored on the server to track the status of users. This data can be stored in clusters, databases and files.
  • Cookie is a mechanism for the client to save user information. It is used to record some user information and also a way to implement Session.

Cors set

Set some specific response headers on the back end.

Set (" access-control-allow-origin ", ctX.headers. Origin); Ctx. set(" access-control-allow-credentials ", true); / / allow the Method to Access my CTX. Set (" Access - Control - Request - Method ", "PUT, POST, GET, DELETE, the OPTIONS"); * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *Copy the code
Cookie about CORS

There are three conditions that need to be met for a cookie to be passed

1. Set withCredentials for Web requests

Here, by default, browsers do not carry cookies on cross-domain requests. But we can pass cookies by setting withCredentials.

Var XHR = new XMLHttpRequest(); xhr.withCredentials = true; / / axios set mode axios. Defaults. WithCredentials = true; Copy the codeCopy the code

2. The Access – Control – Allow – Credentials to true

3. Access – Control – Allow – Origin to a *

The request is made in such a way that chrome can see the return value, but if one of the above is not met, the browser will report an error and fail to get the return value.

Forward agent

The idea of a proxy is to make the interface in the same domain as the current site, taking advantage of the fact that server requests are not cross-domain.

In the json file of each framework, set a proxy option. Different frameworks have different Settings, so when it comes to using them, just search for them yourself. xxx proxy

JSONP

JSONP mainly takes advantage of the fact that script tags have no cross-domain restrictions. Only get method is supported.

How it works: The idea behind JSONP is that you have a callback function name agreed upon with the server. When the server receives the request, it returns a piece of Javascript code in which the callback function is called and the data is passed as an argument. When the web page receives the Javascript code, it executes the callback function, and the data has been successfully transferred to the client.

The front-end code

The front end defines a callback function and passes that callback as a parameter through the request path.

Callback function callback (res) {alert(json.stringify (res, null, 2)); } document.getElementById('btn-4').addEventListener('click', function() { // 2. Cb =Callback var script = document.createElement('script'); cb=Callback var script = document.createElement('script'); Script. The SRC = "http://127.0.0.1:3000/api/jsonp? cb=Callback'; document.getElementsByTagName('head')[0].appendChild(script); });Copy the code

The back-end code

We return the call to that function through the callback function passed by the front end.

router.get('/api/jsonp', (req, res, next) => { var str = JSON.stringify(data); Var script = '${req.query.cb}(${STR})'; // Callback (data); res.send(script) is called when the interface is requested; }); // 4. When receiving the response data, the front-end automatically executes the scriptCopy the code

WebSocket

This approach does not use HTTP response headers by nature, so there are no cross-domain restrictions, and no too much interpretation of the code.

The front-end code

<script> let socket = new WebSocket("ws://localhost:80"); Socket.onopen = function() {socket.send(" what is returned "); }; socket.onmessage = function(e) { console.log(e.data); }; </script>Copy the code

Nginx reverse proxy

Design nGINx for later.

learning

Very detailed cross-domain solution

Nine Cross-domain Implementation Principles (full version)

authentication

cookie

The server uses set-cookie to set cookie information.

Session

Session is another mechanism for recording the session state between the server and client. The session is stored on the server. The session key (sessionId) is stored in the cookie of the client.

According to the preceding process, a session uses cookies to pass session ids for user authentication. In addition, the sessionId can also be passed without cookie, such as the response returned to the client and passed to the server as a parameter to the request for authentication.

session-cookie

You can perform other operations only after logging in. The session id returned by the server is saved in cookie when the front-end requests.

token

Token Authentication Process

  1. The client requests the login using the user name and password
  2. The server receives a request to authenticate the user name and password
  3. After the authentication is successful, the server issues oneTokenAnd then thisTokenSend to the client
  4. Client receivedTokenYou can store it later, like inCookieOr in theLocal Storage
  5. Each time a client requests a resource from a server, it needs to bring a resource that is signed by the serverToken
  6. The server receives the request and then validates the client requestToken(Add Authorization to the request header.) If the authentication is successful, the request data is returned to the client. If the authentication fails, error code 401 is returned.
/ / the front-end axios. Interceptors. Request. Use (config = > {config. Headers. Authorization = window. The sessionStorage. The getItem (" token ") return config })Copy the code

JWT (based on token)

There are many token based solutions, and the most commonly used one is JWT. The principle of JWT is that after the server is authenticated, a JSON object is generated. This JSON object must not be passed to the user naked, so anyone can tamper with this object to send requests. So the JSON object is signed and encrypted by the server and returned to the user. The returned content is a token that the user will carry with him every time he accesses the server.

JWT consists of Header, Payload, and Signature.

  1. The Header section is a JSON object that describes the JWT metadata. The general description information is the encryption algorithm of the Token and the Token type. {“alg”: “HS256″,”typ”: “JWT”} means that the token is encrypted using HS256 and the token type is JWT. This part is basically plain text, and it does a Base64 transcoding of the JSON object into a string. Base64 encoding and decoding are algorithmic and reversible. The header information carries two fields by default.
  2. The Payload section is also a JSON object that holds the actual data that needs to be passed. There are seven official fields, and you can also define private fields in this section. The user name, user identity, and some JWT description fields are generally stored. It’s only Base64 encoded, so you definitely can’t store secret information, like login passwords, in it.
  3. The Signature is the Signature of the first two parts of the information to prevent data tampering. If the first two parts of the information are changed and sent to the server, the server can use the Signature to verify the correctness of the information. The signature requires a key, which is stored on the server and unknown to the user. After the Signature is calculated, the Header, Payload, and Signature parts are combined into a string, with dots (.) between each part. You can return it to the user.
// Front end code // Axios request interceptor, On every request request add JWT authentication information axios. Interceptors. Request. Use (config = > {const token = window. The localStorage. The getItem (" token "); If (token) {// If there is a token, // Bearer is a JWT authentication header information config.headers.common["Authorization"] = "Bearer "+ token; } return config; }, err => { return Promise.reject(err); }); LocalStorage async login() {const res = await axios.post("/login-token", {username: this.username, password: this.password }); localStorage.setItem("token", res.data.token); }, // Logout method: delete JWT async logout() {localstorage.removeItem ("token"); }, async getUser() { await axios.get("/getUser-token"); }Copy the code
// Const JWT = require(" jsonWebToken "); const jwtAuth = require("koa-jwt"); Const secret = "it's a secret"; router.post("/login-token", async ctx => { const { body } = ctx.request; Const userinfo = body.username; // If the user and password are valid, generate a JWT token and pass it to the user const userinfo = body.username; // If the user and password are valid, generate a JWT token and pass it to the user const userinfo = body. Ctx. body = {message: "Login successful ", user: userInfo, // generate token to return to the client token: jwt.sign({data: Exp: math.floor (date.now () / 1000) + 60 * 60}, secret)}; }); // The jwtAuth middleware will take the key to determine whether the JWT is valid. // Then parse the payload in JWT and put it into state. Ctx. state is used for the middleware transmission value. Router. get("/ getuser-token ", jwtAuth({secret}), async CTX => {// The authentication is successful, state.user console.log(ctX.state.user); Cctx. body = {message: "data obtained successfully ", userInfo: cctX.state.user.data}; }) // In this cryptography way, the token does not need to be stored. As long as the server can parse the user information with the key, it indicates that the user is legitimate. // For further permission authentication, you need to determine whether the user is an administrator or a common user.Copy the code

Question: After the token backend is generated, where to put it? Is it transmitted to the front end through data or stored locally through local storage technology, and then retrieved and passed to the back end through authorization request header when the front end accesses it?

Answer: Through the data transfer to the front end, the front end stored in the local, in the future need permission to access, can carry.

Learn from yourself

Precompiled *

  • Find the variable declaration and parameter in the function, and assign the value undefined
  • A parameter is unified with an argument, which is to assign a value to the parameter
  • Look for the function declaration. If there are variables and functions with the same name as the function, the function will override the variable
  • Then execute the code up and down, encounter the same variable name and function name, overwrite each other
  • Then find the assignment statement and assign to the corresponding variable

In short, the declaration of variables is promoted earlier than the declaration of functions

var bar = []; For (var I = 0; i < 10; I++) {bar [I] = function () {/ / each array element is defined as a function of the console, log (I) / / function body}} the bar [1] (); // 10 bar[2](); // Activation Object (AO) can be precompiled by a function before execution. // Activation Object (AO) can be precompiled by a function before executionCopy the code

How to validatelet,constDeclared variables also have variable promotion

Let,const create procedure is promoted, initialization is not promoted

In fact, the variables they declare also have declaration promotion, but there is a temporary dead zone, cannot be accessed before the declaration. In the following example, if it does not exist, then there will be no error and en will be printed

let name = 'zh'
{
  console.log(name) // Uncaught ReferenceError: name is not defined
  let name = 'hy'
}
Copy the code

Load balancing

When a user visits a website, he accesses an intermediate server, then asks the intermediate server to select a server with less pressure in the server cluster, and then directs the access request to the selected server. This ensures that the pressure on each server in the server cluster is balanced.

Execution context

There are three types of execution context in JavaScript.

  • Global execution context— This is the default or underlying context. Any code that is not inside a function is in the global context. It does two things: create a global Window object (in the case of the browser), and setthisIs equal to the global object. There is only one global execution context in a program.
  • Function execution context – Every time a function is called, a new context is created for that function. Each function has its own execution context, which is created when the function is called. There can be any number of function contexts. Whenever a new execution context is created, it performs a series of steps in the order defined (discussed later).
  • The Eval function executes context– perform inevalThe code inside the function also has its own execution context, which is not often used by JavaScript developerseval.

Execution stack

When the program starts to run, the JS engine will create a stack structure, and create a global execution context to push it on the stack, when a function call, will create a function execution context, push it on the stack, and so on. When the function finishes executing, the execution context for that function is popped off the stack, giving the execution authority to the top element. Until the program finishes executing, the global execution context is popped off the stack.

A call stack is a data structure. If we run a function, it puts it on the top of the stack. When you return from this function, it pops that function off the top of the stack, and that’s what the call stack does.

scope

The scope chain is connected by the execution context. The variable objects in the environment stack form a scope chain from top to bottom. Its purpose is to ensure orderly access to all variables and functions that the execution environment has access to.

Lexical scope means that the outer scope of an inner function is determined when it is defined.

(function autorun(){ let x = 1; // When a variable is accessed in this function, the scope is determined when it is defined, not when it is executed. function log(){ console.log(x); }; function run(fn){ let x = 100; fn(); } run(log); / / 1}) ();Copy the code

Lexical environment

Contains the environment logger and references to the external environment

  1. The environment logger is the actual location where variable and function declarations are stored.
  2. A reference to an external environment means that it has access to its parent lexical environment (scope).

Js event loop

If the content to be expressed by the map is expressed in words:

  • Synchronous and asynchronous tasks go to different “venues”, synchronously to the main thread and asynchronously to the Event Table and register functions.
  • When the specified thing is done, the Event Table moves the function to the Event Queue.
  • If the task in the main thread is empty after execution, it will go to the Event Queue to read the corresponding function and enter the main thread for execution.
  • This process is repeated over and over again, known as an Event Loop.

MacroTask

  • scriptAll code,setTimeout,setInterval,setImmediate(Browser temporarily not support, only IE10 support, specific visibleMDN),I/O,UI Rendering.

MicroTask

  • Process.nextTick (unique to Node),PromiseThe then callback,Object. Observe (waste),MutationObserver(View the specific usage modehere)

The setTimeout function adds the task to the Event Queue after a specified period of time. Instead of executing at the specified time, only when the main thread is free, go back and execute the waiting events in the Event Queue.

SetTimeout (fn,0) specifies the earliest available idle time for a task to be executed on the main thread. This means that the task will be executed as soon as all synchronization tasks in the main thread execution stack are completed and the stack is empty.

Macro task and microtask execution order:

Run the project logic —-> add the callback functions defined by setTimeout, setInterval, etc to the macro task queue, Will be executed promiese callback to join micro task queue — — — — — — — — — — – > the main logic has been completed – > callback of executing the task queue — — — — — — — — > macro tasks in the queue callback

The entire length of the script code is a macro task, in the process of macro mission will continue to have a mission to join the micro micro task queue, after when performing a macro task to see if there are any micro tasks at micro task queue, if there is the whole first task performed, and then in the execution of a macro task, so the past form the event loop.

process.nextTick()Although it is part of the asynchronous API.process.nextTick()Technically, it is not part of the event loop.

  • process.nextTick()Method will becallbackAdded to thenext tickThe queue. Once the tasks of the current event polling queue are complete, innext tickAll in the queuecallbacksWill be called in turn.
  • Process. nextTick should be independent of the Event Loop. It should be a microtask, but it should have its own queue, and the callback functions in the queue will be executed before the functions in the microtask queue. For example, if you put process.nextTick under promise. then, it will still execute first.

// Note that the promise is executed in the main thread before it changes state, and in the then, await and await tasks it is executed in the microtask.console.log('1');
​
setTimeout(function () {
  console.log('2');
  process.nextTick(function () {
    console.log('3');
  })
  new Promise(function (resolve) {
    console.log('4');
    resolve();
  }).then(function () {
    console.log('5')})// process.nextTick(function () {
  // console.log('3');
  // })
})
​
​
process.nextTick(function () {
  console.log('6');
})
​
​
new Promise(function (resolve) {
  console.log('7');
  resolve();
}).then(function () {
  console.log('8')})setTimeout(async function () {
  console.log('9');
  // process.nextTick(function () {
  // console.log('10');
  // })
  // new Promise(function (resolve) {
  // console.log('11');
  // resolve();
  // }).then(function () {
  // console.log('12')
  // })
​
  let result = await Promise.resolve("11")
  console.log(result)
  console.log("12")}); (async() = > {console.log('13');
​
    let result = await Promise.resolve("14")
    console.log(result)
    console.log("15")
  })()
​
// 1 7 13 6 8 14 15 2 4 3 5 9 11 12.
Copy the code

Js execution mechanism

In-depth MutationObserver

Learn the Event loop once

mutationobserver

The Mutation Observer API is used to monitor DOM changes. The API is notified of any changes to the DOM, such as changes in nodes, changes in attributes, and changes in text content.

Conceptually, it is close enough to an event that the Mutation Observer event is triggered when a DOM change occurs. However, it differs from events in one essential way: events are triggered synchronously, meaning that a change in the DOM triggers the corresponding event immediately. The Mutation Observer is an asynchronous trigger, in that changes to the DOM are not triggered immediately, but are not triggered until all current DOM operations are complete.

The Mutation Observer has the following characteristics.

  • It waits for all script tasks to complete before it runs (that is, asynchronously triggered).
  • Instead of dealing with DOM changes individually, it encapsulates the DOM change record as an array for processing.
  • It can either observe all types of CHANGES in the DOM or specify that only one type of change is observed.

The MutationObserver constructor

To create an observer object with new, the change constructor needs to pass a callback function.

var observer = new MutationObserver(callback);
Copy the code

This callback takes two arguments, one: changing the DOM array, and two: the observer instance.

let callback = function (mutations, self) {
  
}
Copy the code

Example method:

1. Observe (change element, observe option)

let odiv = document.getElementById('#div');
​
let  options = {
  'childList': true,
  'attributes':true
} ;
​
observer.observe(odiv, options);
Copy the code

Here are the properties of the options object and their descriptions:

attribute type describe
childList Boolean Whether to observe changes in child nodes
attributes Boolean Whether to observe property changes
characterData Boolean Whether the node content or node text changes
subtree Boolean Whether to observe all offspring node changes
attributeOldValue Boolean Observe whether the attribute value before the change is recorded when the attributes change
characterDataOldValue Boolean Observe whether to record the property value before the characterData change
attributeFilter Array Represents a specific attribute (such as [‘class’,’ SRC ‘]) that needs to be observed and is ignored if the attribute changes outside this array

Note:

  1. Adding an observer to a node, just like using the addEventListener method, has no effect on adding the same observer multiple times, and the callback function will still only fire once. However, if you specify different options objects, you are treated as two different observers.

  2. One or more of childList, Attributes, and characterData must be specified in the listening options.

    Failed to execute 'observe' on 'MutationObserver': The options object must set at least one of 'attributes', 'characterData', or 'childList' to true.
    Copy the code

2.disconnect()

This method stops listening for DOM changes.

observer.disconnect();
Copy the code

3.takeRecords()

Used to clear the change record, that is, no longer process the unprocessed changes. This method returns an array of change records.

// Save all changes that are not processed by the observer observer.takerecords ();Copy the code

MutationRecord object

That is, the state value generated each time the DOM changes.

attribute type describe
type String Depending on the change type, the value is attributes, characterData, or childList
target Node The DOM node that changed
addedNodes NodeList The added node, or null
removedNodes NodeList Deleted node, or null
previousSibling Node The previous sibling of the node being added or removed, or null
nextSibling Node The second sibling of the node that was added or removed, or null
attributeName String The local name of the changed property, or null
attributeNamespace String The namespace of the changed property, or null
oldValue String If type is attributes, the value of the attribute before the attribute change is returned; If the type is characterData, the text data before the change of the object is returned. If type is childList, null is returned

Refer to the JavaScript Standard Reference Tutorial (Alpha))DOM model Mutation Observer API

Optimize the logical judgment in JS

Plain: if-else if-else, switch-case

Unary judgment: Map, object

Map: [[condition, logical parameter], [condition, logical parameter],…]

Object: {condition: logical argument}

/** * Button click event * @param {number} status */ const onButtonClick = (status)=>{if(status == 1){sendLog('processing')) jumpTo('IndexPage') }else if(status == 2){ sendLog('fail') jumpTo('FailPage') }else if(status == 3){ sendLog('fail') jumpTo('FailPage') }else if(status == 4){ sendLog('success') jumpTo('SuccessPage') }else if(status == 5){ sendLog('cancel') jumpTo('CancelPage') }else { sendLog('other') jumpTo('Index') } }Copy the code
const actions = { '1': ['processing','IndexPage'], '2': ['fail','FailPage'], '3': ['fail','FailPage'], '4': ['success','SuccessPage'], '5': ['cancel','CancelPage'], 'default': ['other','Index'],} /** * button click event * @param {number} status 1 open group on 2 failure 3 goods sold out 4 open groups successfully cancel * / 5 system const onButtonClick = (status) = > {let action = actions [status] | | actions['default'], logName = action[0], pageName = action[1] sendLog(logName) jumpTo(pageName) }Copy the code
const actions = new Map([ [1, ['processing','IndexPage']], [2, ['fail','FailPage']], [3, ['fail','FailPage']], [4, ['success','SuccessPage']], [5, ['cancel','CancelPage']], ['default', ['other','Index']]) /** * Button click event * @param {number} status 1 open group on 2 failure 3 goods sold out 4 open groups successfully cancel * / 5 system const onButtonClick = (status) = > {let action = actions. Get (status) | | actions.get('default') sendLog(action[0]) jumpTo(action[1]) }Copy the code

Multi-level judgment: Map

Map: [[condition, () => {}], [condition, () => {}],….]

  • A condition can be a string of conditions combined
  • A condition can be an object
  • Conditions can be re to match more processing of different conditions for the same logic
  • .

Object: {condition: () => {}, condition: () => {},… }

  • A condition can be a string of conditions combined
@param {number} status @param {number} status @param {number} status @param {number} status @param {number} status @param {number} status @param {number} status */ const onButtonClick = (status,identity)=>{if(status == 'guest'){//do STH}else if(status == 2){ //do sth }else if(status == 3){ //do sth }else if(status == 4){ //do sth }else if(status == 5){ //do sth }else { //do sth } }else if(identity == 'master') { if(status == 1){ //do sth }else if(status == 2){ //do sth }else if(status == 3){ //do sth }else if(status == 4){ //do sth }else if(status == 5){ //do sth }else { //do sth } } }Copy the code
const actions = new Map([ ['guest_1', ()=>{/*do sth*/}], ['guest_2', ()=>{/*do sth*/}], ['guest_3', ()=>{/*do sth*/}], ['guest_4', ()=>{/*do sth*/}], ['guest_5', ()=>{/*do sth*/}], ['master_1', ()=>{/*do sth*/}], ['master_2', ()=>{/*do sth*/}], ['master_3', ()=>{/*do sth*/}], ['master_4', ()=>{/*do sth*/}], ['master_5', ()=>{/*do sth*/}], [' default ', () = > {/ * do STH * /}],]) / * * * * @ param button click event {string} identity identification: * @param {number} status */ const onButtonClick = (identity,status)=>{let action = actions.get(`${identity}_${status}`) || actions.get('default') action.call(this) }Copy the code
const actions = {
  'guest_1':()=>{/*do sth*/},
  'guest_2':()=>{/*do sth*/},
  //....
}
​
const onButtonClick = (identity,status)=>{
  let action = actions[`${identity}_${status}`] || actions['default']
  action.call(this)
}
​
Copy the code
const actions = new Map([ [{identity:'guest',status:1},()=>{/*do sth*/}], [{identity:'guest',status:2},()=>{/*do sth*/}], //... ] ) const onButtonClick = (identity,status)=>{ // ... Actions according to the [[], [], []] of internal array [], [], [] let action = [actions].... The filter (((key, value)) = > (key. The identity = = identity && key.status == status)) action.forEach(([key,value])=>value.call(this)) }Copy the code
const actions = ()=>{ const functionA = ()=>{/*do sth*/} const functionB = ()=>{/*do sth*/} return new Map([ [{identity:'guest',status:1},functionA], [{identity:'guest',status:2},functionA], [{identity:'guest',status:3},functionA], [{identity:'guest',status:4},functionA], [{identity:'guest',status:5},functionB], //... ] ) } const onButtonClick = (identity,status)=>{ let action = [...actions()].filter(([key,value])=>(key.identity == identity && key.status == status)) action.forEach(([key,value])=>value.call(this)) }Copy the code
const actions = ()=>{
  const functionA = ()=>{/*do sth*/}
  const functionB = ()=>{/*do sth*/}
  const functionC = ()=>{/*send log*/}
  return new Map([
    [/^guest_[1-4]$/,functionA],
    [/^guest_5$/,functionB],
    [/^guest_.*$/,functionC],
    //...
  ])
}
​
const onButtonClick = (identity,status)=>{
  let action = [...actions()].filter(([key,value])=>(key.test(`${identity}_${status}`)))
  action.forEach(([key,value])=>value.call(this))
}
​
Copy the code

Learn from nuggets Think

promise

Common static methods: All, race, resolve, reject.

Common instance methods: then, catch.

In the case of the all method, the argument is an array of promises. If the promises define their own THEN,catch methods and return a value, whether it succeeds or fails, then the ALL method then executes and the catch does not.

const p1 = new Promise((resolve, reject) => { resolve('hello'); }) .then(res => res) .catch(e => e); Const p2 = new Promise((resolve, reject) => {throw new Error(' Error '); }) .then(result => result) .catch(); // If there is no return value, the catch method of all will execute. Promise.all([p1, p2]) .then(result => console.log(result)) .catch(e => console.log(e)); // Execution result Error: Error is reportedCopy the code

Axios has one more simplification. Encapsulate the received request parameters into a function that receives spread()

function getUserAccount() { return axios.get('/user/12345'); } function getUserPermissions() { return axios.get('/user/12345/permissions'); } axios.all([getUserAccount(), getUserPermissions()]).then(axios.spread(function (acct, perms) {// both requests are now complete});Copy the code

A promise is executed only once, but a promise’s THEN and catch can be executed multiple times.

const promise = new Promise((resolve, reject) => { setTimeout(() => { console.log('once') resolve('success') }, 1000) }) const start = Date.now() promise.then((res) => { console.log(res, Date.now() -start)}) promise.then((res) => {console.log(res, date.now () -start)}) // Only prints onceCopy the code

A.then or.catch argument is expected to be a function, and passing a non-function will cause value penetration.

Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .then(console.log)
Copy the code

An error thrown by.then is not caught by the second argument function, but only by a later catch.

Promise.resolve().then(function success (res) {// Note that the error is thrown, not returned. throw new Error('error') }, function fail1 (e) { console.error('fail1: ', e) }) .catch(function fail2 (e) { console.error('fail2: ', e) })Copy the code

Write a promise

Class Promise {constructor(executor) {// let _this = this _this.status = 'pending' // Save the current state _this.value = _this.error = undefined// on failure, Function resolve(value) {if (_this.status === 'pending') {_this.status = 'resolved' _this.value = value}} function resolve(value) {if (_this.status === 'pending') {_resolve. function reject(error) { if (_this.status === 'pending') { _this.status = 'rejected' _this.error = error } } This is very depressing. This is very depressing. This is very depressing. onRejected) { let _this = this if (_this.status === 'resolved') { onfulfilled(_this.value) } else if (_this.status === 'rejected') {onRejected(_this.error)}}} Let promise = new Promise((resolve, reject) => {setTimeout(() => {resolve(' I'm Zhang Ho ') reject(' I'm Li Long Min ')}, }).then(res => {console.log(res)}, res => {console.log(res)}Copy the code

When the instance is new, the code in the executor is executed immediately, but the code in setTimeout is executed later. In other words, when the then method is executed, the Promise state is still pending without being changed. Therefore, we need to judge pending state as well, and since the code may be asynchronous, we need to cache the callback function, and then method can be used multiple times, so we need to store multiple callbacks, so we use an array here.

Create two arrays to store the resolve and failed callbacks.

_this.onResolvedCallbacks = []; Mysql_this.onrejectedcallbacks = []; // Mysql_rejectedCallbacks = []; Reject (); // Reject () ¶Copy the code

The pending judgment is determined in the THEN and the callbacks in the THEN are stored in onResolvedCallbacks and onRejectedCallbacks respectively

If (_this.status === 'pending') {if (_this.status === 'pending') {if (_this.status === 'pending') {if (_this.status === 'pending') {if (_this.status === 'pending') {if (_this.status === 'pending') {if (_this.status === 'pending') {if (_this.status === 'pending') { In performing _this. OnResolvedCallbacks. Push (function () {onfulfilled (_this. Value)}) / / cache fails the callback function, when the state changes to the rejected, In performing _this. OnRejectedCallbacks. Push (function () {onRejectedCallbacks (_this. Error)})}Copy the code

A successful callback for asynchronous execution is cached in the resolve method and a failed callback for asynchronous execution is cached in the reject method

Function resolve(value) {if (_this.status === 'pending') {_this.status = 'resolved' _this.value = value when successful When (resolve()) is called, The cache before the callback function will be one by one call. _this onResolvedCallbacks. The map (callback = > callback ())}} function reject (error) {if (_this. Status = = = 'pending') {_this.status = 'rejected' _this.error = error // When a failed function (reject()) is called, The cache before the callback function will be one by one call. _this onRejectedCallbacks. The map (callback = > callback ())}}Copy the code

To prevent a promise from failing during execution, we need to do error handling

    try {
      executor(resolve, reject)
    } catch (e) {
      console.log(e)
    }
Copy the code

A chained call to a PROMISE. Since the state of the promise changes, the promise returned by then is an entirely new one.

A little complicated!! Promise’s other prototyping methods

// Catch error method, catch method on prototype, Function (callback) {return this.then(null, callback)} Receive a Promise array promises, return a new Promise, iterate through the array, All = function (Promise) {return new Promise(function (Promise, Promise) return new Promise(function (Promise, Promise)) reject) { let arr = []; //arr is the result of the final return value let I = 0; Function processData(index, y) {arr[index] = y; if (++i === promises.length) { resolve(arr); } } for (let i = 0; i < promises.length; I++) {promises [I]. Then, the function (y) {processData (I, y)}, reject)}})} / / as long as there is a promise for success Even if successful. Promise. Race = function (Promise) {return new Promise(function (resolve, reject) {for (var I = 0; i < promises.length; Make promises[I].then(resolve,reject)}})} // make promises. Resolve = function(value){return new Promise(function(resolve,reject){ resolve(value); })} // generate a failed promise. Reject = function(error){return new Promise(function(resolve,reject){reject(error); })}Copy the code

Learning from the

I promise I will

RocYoung’s analysis of Promise

Front-end route hops

The Hash method is to have a # in the route. The main principle is to listen for the change of URL path identifier after the # and trigger the browser HashChange event, and then obtain the current path identifier by obtaining location. Hash, and then perform some route hops.

Class RouterClass {constructor() {this.isback = false this.routes = {} class RouterClass {constructor() {this.isback = false this.routes = {} class this.currenturl = "// Cb this.historystack = [] // Hash stack window.addeventListener ('load', () => this.render()) window.addEventListener('hashchange', () => this.render())} /* initialize */ static init() {window.router = new RouterClass()} /* Record path to cb */ route(path, Cb) {enclosing routes/path = cb | | function () {}} / * into the stack current hash, perform cb * / render () {if (this. IsBack) {/ / if it is from the backoff to, After put false return enclosing isBack = false / / other operations has been done in the backoff method return} this. CurrentUrl = location. The hash. Slice (1) | | '/' // Add each path to the stack, This.historystack.push (this.currenturl) this.routes[this.currenturl]()} /* Route back */ back() {this.isback = True this.historystack.pop () // Remove the current hash and fall back to the last const {length} = this.historystack if (! Length) return // If there are no paths in the stack, Let prev = this.historystack [length-1] hash location.hash = '#${prev}' // For manual jump to work properly, add a # to the current path, This.currenturl = prev this.routes[prev]() // Execute the corresponding cb}}Copy the code

history

  1. history.go(n): Indicates a route hop. For example, n is2Is to move 2 pages forward, n is2 -Is to move back 2 pages, n 0 is to refresh the page
  2. history.back(): Indicates that the route is backwardhistory.go(-1)
  3. history.forward(): Indicates that the route is forwardhistory.go(1)
  4. history.pushState(): Adds a route history record. If the cross-domain url is set, an error message is displayed and the browser has records.
  5. history.replaceState(): replaces the information on the current page in the route history record. The browser does not record the information.
  6. popstateEvent: Triggered when the history of an activity changespopstateEvent, which is also triggered when the browser’s forward and back buttons are clicked or when the previous three methods are called. See alsoMDN

What is a closure?

A closure is implemented as a structure that stores a function (usually its entry address) and an associated context (equivalent to a symbol lookup table). An environment is a correspondence between a number of symbols and values, including both constraint variables (symbols bound within the function) and free variables (defined outside the function but referenced within the function). Some functions may not have free variables. A major difference between closures and functions is that when a closure is captured, its free variables are determined at capture time, so that it works even outside the context in which it was captured. Value processing at capture can be either a value copy or a name reference.

Closures, on the other hand, are meant to include both the function pointer and the environment. In compilation optimization, closures that do not capture free variables can be optimized to normal functions.

When are variables introduced in closures destroyed?

External variables accessed in closures are stored in heap memory.

The life cycle of the variable depends on the life cycle of the closure. Variables in the outer scope referenced by the closure survive until the closure function is destroyed. If a variable is referenced by multiple closures, it will not be destroyed until all closures have been garbage collected.

wikipedia

The return value of an array method that has a higher-order function

Map: The return value of this method is an array of the return values of the callback function.

Filter: The return value of this method is an array of qualified return values from the callback function.

let arr = [1, 2, 3, 4] let resArr = arr.filter(item => {if (item > 2) {return item}}) console.log(resArr)Copy the code

ForEach: This method returns no value.

Reduce: The value returned by this method is the accumulated value of the accumulator (the first argument of the callback function).

let arr = [ { id: 1, name: 'zh', age: 20 }, { id: 2, name: 'hy', age: 19 }, { id: 3, name: 'llm', age: 19 } ] let reArr = arr.reduce((pre, next) => { if (next.age === 19) { return pre.concat(Object.assign({}, next, { sex: 'female'}))} return []// There must be a return value, otherwise there will be an error, because each iterated item requires a return value. If a pre is used, an error will be reported. The value of pre is the value returned by each callback. }, []) console.log(reArr) // {id: 2, name: "hy", age: 19, sex: "female"} // {id: 3, name: "llm", age: 19, sex: "female"}Copy the code

Every: This method returns a Boolean value, true only if all the elements in the array meet the condition, false otherwise.

let arr = [1, 2, 3, 4]
​
let resArr = arr.every(item => item >= 1)
​
console.log(resArr)
Copy the code

Some: This method returns a Boolean value, meaning true as long as one element in the array satisfies the condition, and false otherwise.

Find: This method returns the first value that satisfies the condition.

FindIndex: This method returns the index of the first value that satisfies the condition.

URLsearchParams

URLsearchParams(URL) is used to parse URL parameters.

//URLSearchParams lets url = '? name=zh&age=20'; Let searchParams = new URLSearchParams(URL)// console.log(searchParams)// Map console.log(arr) //[ [ 'name', 'zh' ], [ 'age', '20' ] ]Copy the code

Gets a single parameter.

searchParams.get('name')
Copy the code

Check whether the parameter exists.

searchParams.has('sex') // false
searchParams.has('age') // true
Copy the code

Add parameters.

searchParams.append('sex', 'male') console.log(searchParams)//URLSearchParams { 'name' => 'zh', 'age' => '20', 'sex' => 'male' } console.log(url)//? Name =zh&age=20, note that this parameter is not added to the URL, but it will be parsedCopy the code

Delete parameters.

searchParams.delete('sex');
searchParams.has('sex'); // false
Copy the code

Modify parameters.

searchParams.set('age', 22)
console.log(searchParams)//URLSearchParams { 'name' => 'zh', 'age' => '22', 'sex' => 'male' }
Copy the code

Parsed parameters are converted to query strings.

searchParams.toString() //name=zh&age=22&sex=male
Copy the code

Override array methods

Override the map method to use the for loop

Const selfMap = function (fn, context = this) {// the context is treated as this in the fn function. If no context is passed in, then this is the array called. let arr = Array.prototype.slice.call(context) let mappedArr = Array() for (let i = 0; i < arr.length; I++) {// check for sparse arrays if (! arr.hasOwnProperty(i)) continue; mappedArr[i] = fn.call(context, arr[i], i, this) // console.log(context) } return mappedArr } Array.prototype.selfMap = selfMap let resArr = [0, 0, 0, 1]. SelfMap (number => number * 2, [2, 3, 4]) console.log(resArr)//[4,6,8].Copy the code

My misinterpretation of the map method is that the context simply changes the point to this, not the array in which the function is executed.

The second method, the map method, I feel that I should start the context with this, and then pass the context into the slice method, so that the value of this changes when the second parameter is passed. Let resArr = [0,0,0, 1]. SelfMap (number => number * 2, [2, 3, 4]) console.log(resArr) Const selfMap = function (fn, 4,6,8); The context = this) {/ / the context will be here as a fn in the function this let arr = Array. The prototype. Slice. The call (context) let mappedArr = Array () (let i = 0; I number * 2, [2, 3, 4]) console.log(resArr)

Here is the js native Map test

Let resArr = [4,5,6].map(function (item, index, arr1) {console.log(arr1)//[4,5,6] console.log('this', This) / / [1, 2, 3] return item * 2 / / role is (4 and 6)}, [1, 2, 3]) console. The log (resArr)Copy the code

Override the map method. Use the Reduce method.

const selfMap = function (fn, context) { let arr = Array.prototype.slice.call(this) return arr.reduce((pre, cur, Index) => {// return the pre, then return the pre, then expand the pre. // It returns the pre every time, and expands the pre every time. Each expansion repeats the elements of the pre returned last time, so it should repeat a lot. return [...pre, fn.call(context, cur, index, this)] }, []) } Array.prototype.selfMap = selfMap let resArr = [1, 2, 3].selfMap(item => item * 2) console.log(resArr)Copy the code

Solution: since the array’s memory address is the same, the Pre’s memory address is the same, just expanded to the value of the last assignment.

let a = new Array(10)
let resArr = []
for (let i = 0; i <= a.length; i++) {
  let arr = [i]
  resArr = [...arr, 2, 3, 4]
}
​
console.log(resArr)//[ 10, 2, 3, 4 ]
Copy the code

Override the filter method to use the for loop.

const selfFilter = function (fn, The context) {/ / to deal with an Array of arr = Array. The prototype. Slice. The call (this) let resArr = Array () for (let I = 0; i < arr.length; I++) {// check whether the returned condition is correct and add it to the array. fn.call(context, arr[i], i, this) && resArr.push(arr[i]) } return resArr } Array.prototype.selfFilter = selfFilter let arr = [1, 2, 3] let arr1 = arr.selfFilter(item => item > 2) console.log(arr1)Copy the code

Rewrite filter to use Reduce.

const selfFilter = function (fn, context) {
  let arr = Array.prototype.slice.call(this)
  let resArr = []
  return arr.reduce((pre, cur, index) => {
    // if (fn.call(context, cur, index, this)) {
    //   resArr.push(cur)
    //   return resArr
    // }
    return fn.call(context, cur, index, this) ? [...pre, cur] : [...pre]
  }, [])
}
​
Array.prototype.selfFilter = selfFilter
​
let arr = [0, 2, 3]
​
let arr1 = arr.selfFilter(item => item > 2)
​
console.log(arr1)
Copy the code

Rewrite some, using the for loop.

const someFilter = function (fn, context) {
  let arr = Array.prototype.slice.call(this)
  for (let i = 0; i < arr.length; i++) {
    if (fn.call(context, arr[i], i, this))
      return true
  }
  return false
}
​
Array.prototype.someFilter = someFilter
​
let arr = [1, 2, 3]
​
let arr1 = arr.someFilter(item => item > 2)
console.log(arr1)
Copy the code

Override the flat method to use reduce.

const selfFlat = function (depth = 1) { let arr = Array.prototype.slice.call(this) // let resArr = [] if (depth === 0) Return arr return reduce((pre, cur) => {if (array.isarray (cur)) {return [...pre,...selfFlat. depth - 1)] } else { return [...pre, cur] } }, []) }Copy the code

Es6’s object-oriented class syntax

The class interior of ES6 is based on parasitic combinatorial inheritance

function inherit(subType, superType) { subType.prototype = Object.create(superType.prototype, { constructor: { value: SubType, Enumerable: false, different: true, writable: true}}) // Allows static methods of the class to be inherited, too. Object.setPrototypeOf(subType, superType) }Copy the code

Curryization of a function

J is a function that can be called step by step by calling the following function.

It’s a third order function. Call the function directly after the meeting.

Currying converts a multi-argument function into multiple single-argument functions, but now we can pass not only one argument, but two arguments at a time.

Var curry = function (fn) {var curry = function (fn) {var curry = function (fn) { var args = [].slice.call(arguments, 1); Return function () {// Concatenate all the arguments passed in. var newArgs = args.concat([].slice.call(arguments)); // We call each function separately, and we call the last function directly. return fn.apply(this, newArgs); }; };Copy the code

Some bugs have been fixed. It can be called once or multiple times.

Function curry(fn, args) {// access the number of arguments in fn. let length = fn.length; args = args || [] return function () { let _args = args.slice(0), arg, i; for (i = 0; i < arguments.length; I++) {// add arguments from the second function to the first argument. arg = arguments[i]; _args.push(arg)} // Compare the total parameter to the fn parameter. Until the function parameters are satisfied to be called at the same time as the currie function parameters. if (_args.length < length) { return curry.call(this, fn, _args); } else { return fn.apply(this, _args) } } } let fn = curry(function (a, b, c) { console.log([a, b, c]) }) fn("a", "b", "c") fn("a")("c", "b", "d")Copy the code

Reference from the JavaScript topic on Function currying

The length and arguments of the function

Note: length indicates the number of parameters, and arguments indicates the number of arguments.

What are the advantages of the Virtual Dom?

If the performance of Dom manipulation was that bad, jQuery wouldn’t be alive today. If the performance of Dom manipulation was that bad, jQuery wouldn’t be alive today. So interviewers are more likely to hear what problems VDOM is trying to solve and why frequent DOM manipulation causes poor performance.

First we need to know:

The DOM engine and JS engine are independent, but they work in the same thread (main thread). The JS code calling the DOM API must suspend the JS engine, convert the incoming parameter data, activate the DOM engine, and then convert the return value after DOM redrawing. If there are frequent DOM API calls, and the browser manufacturer does not do “batch processing” optimization, the unit cost of switching between engines will quickly accumulate. If there are DOM API calls forced to redraw, re-calculating the layout and redrawing the image will cause greater performance consumption.

The second is the difference and optimization between VDOM and real DOM:

  1. The virtual DOM does not perform typesetting and redrawing operations immediately
  2. The virtual DOM is frequently modified, and then the parts that need to be modified in the real DOM are compared and modified at one time. Finally, typesetting and redrawing are carried out in the real DOM to reduce the loss of excessive DOM node typesetting and redrawing
  3. Virtual DOM can effectively reduce the redrawing and typesetting of a large area of the real DOM, because the final difference with the real DOM can only render part

How to select pictures

The name of the introduce Not suitable for Suitable for
JPEG It is often used to store and transfer photos on the Internet Line graphics, text graphics, icon graphics, it does not support transparency Colorful photos, color maps with large focus, banner BANNR maps, irregular structure graphics.
PNG Raster graphics, originally designed as a replacement for GIFs, have larger files and support for translucent and transparent features. Due to lossless storage, color images are too bulky to be suitable Solid color, transparent, line drawing, icon. The edges are clear and there are large areas of the same color. Fewer colors but need to be translucent.
Webp Can insert multiple frames, achieve animation effect, can set transparency, better than GIF animation. Maximum processing 256 colors, not suitable for color pictures Suitable for graphics and translucent images
Gif Raster graphics, supports 256 colors, supports only full transparency and full opacity, if you need to compare general purpose animations, GIF is the only option. Each pixel has only 8 bits, which is not good for storing color pictures. Animations and ICONS

JSON.stringify()

Distortions can occur in the following situations.

ToJSON is called if the object has a toJSON method.

const arr = [function () { }, undefined, NaN, 2] arr.tojson = () => {return 'you are distorted'} const toJSON = json.stringify (arr) console.log(toJSON)// You are distortedCopy the code

: in the array is Undefined/Symbol will become null/Function data type. Infinity/NaN will also be null.

const arr = [function () { }, undefined, NaN, Infinity, Symbol(), 2]
​
const tojson = JSON.stringify(arr)
​
console.log(tojson)//[null,null,null,null,null,2]
Copy the code

: in the object attribute value is Undefined/Symbol/Function data types, attributes and values are not converted to a string. If the property value is Infinity/NaN, the property value will become null.

const obj = {
  name: undefined,
  age: Symbol(),
  sex: function () { },
  address: NaN,
  friend: Infinity,
  hobbit: 'llm'
}
​
console.log(JSON.stringify(obj))//{"address":null,"friend":null,"hobbit":"llm"}
Copy the code

Values of the date data type are called toISOString.

Const tojson1 = json.ify (time) const tojson = time.toisoString () const tojson1 = json.ify (time) The console. The log (tojson1) / / ": the 2020-08-26 T03 exiles. 440 z" console. The log (tojson) / / 2020-08-26 T03: exiles. 440 zCopy the code

Complex data types that are not arrays/objects/functions/dates become empty objects.

const reg = new RegExp("at", 'i')
​
const res = JSON.stringify(reg)
​
console.log(res)//{}
Copy the code

Json. stringify takes a second argument, replacer, which can be either an array or a function that specifies which properties should be processed and which should be excluded during object serialization.

function replacer(key, value) { if (typeof value === "string") { return undefined; } return value; } var foo = {foundation: "Mozilla", model: "box", week: 45, transport: "car", month: 7}; var jsonString = JSON.stringify(foo, replacer); console.log(jsonString)// {"week":45,"month":7} var foo = {foundation: "Mozilla", model: "box", week: 45, transport: "car", month: 7}; console.log(JSON.stringify(foo, ['week', 'month'])); // {"week":45,"month":7}Copy the code

Circular references throw errors.

What is a circular reference?

Object whose property value is equal to the parent reference.

const obj1 = {
  a: null,
  name: 'zh'
}
obj1.a = obj1
​
const res = JSON.stringify(obj1)
console.log(res)//TypeError: Converting circular structure to JSON
Copy the code

Implement the Call method

The core idea is to treat a function as a method to bind to an object, then execute the function, and finally remove the method from the bound object.

const selfCall = function (context, ... Args) {// This step equals this, but this is an object. // This is an instance of the Function class. let func = this context || (context = window) if (typeof func ! == 'function') throw new TypeError('this is not a function') let caller = Symbol('caller') context[caller] = func let res = context[caller](... args) delete context[caller] return res } Function.prototype.selfCall = selfCall let name = 'llm' let obj = { name: 'zh' } function sayName() { console.log(this.name) } sayName.selfCall(obj)Copy the code

Implement the Apply method

The main thing here is to identify the wrong arguments, and then key the array arguments passed in, and expand the call in the function passed in.

function selfApply(context, arr) { let func = this; context = context || window; if (typeof func ! == 'function') throw new TypeError('this is not a function') let caller = Symbol('caller') context[caller] = Func // Let res; if (! arr) { res = context[caller]() } else { res = context[caller](... Arr)} / / delete the Function delete context [caller] return res} Function. The prototype. SelfApply = selfApply; let name = 'llm' let obj = { name: 'zh' } function sayName(a, b) { console.log(this.name, a, b) } sayName.selfApply(obj, [1, 2])Copy the code

Type conversion

  1. Convert the original value to a Boolean value. There are only six cases where it’s converted to false.

    false , undefined , NaN , null , "" , 0
    Copy the code

    Note that Boolean() returns false if it passes no arguments.

  2. The original value is converted to a number.

    If the Number function passes no arguments, return +0; if it does, call ToNumber(value).

    Note that ToNumber represents a method on the underlying specification implementation and is not directly exposed.

The parameter types The results of
Undefined NaN
Null + 0
Boolean If the argument is true, return 1. If the argument is false, +0 is returned
Number Returns an equal value
String This one’s a little bit more complicated. Let’s do an example

In the case of the Number conversion, it is to convert the parameters passed in to positive or floating point numbers as much as possible. Ignore all leading zeros. If there are non-numeric characters in the argument, it will be converted to NaN. But only if everything passed in is blank. Leading and leading Spaces are ignored.

Number("   ")//0
Number("123  123")//NaN
Number("123   ")//123
Number("   123")//123
Copy the code
  1. The original value is converted to a string.

    Call the String() method directly, passing no arguments, and return an empty String. If there are arguments, call ToString(value). This method is also implemented at the bottom level and is not exposed.

    The parameter types The results of
    Undefined “undefined”
    Null “null”
    Boolean Returns “true” if the argument is true. If the argument is false, return “false”
    Number Again, it’s a little bit more complicated, so you can see examples
    String Returns an equal value
  2. Object to a string and a number.

    The toString() method returns a string that reflects the object.

    let arr = [1, Log (arr1.tostring ())//"1,2" console.log(arr1.tostring ())//"" // console.log(null.tostring ())// error // Console.log ((NaN).tostring ())//"NaN" let STR = "" let str1 = "" let str2 =" "zh" console.log(str.toString())//"" console.log(str1.toString())//"" console.log(str2.toString())//"zh" console.log(true.toString())//true console.log(false.toString())//falseCopy the code

    Another function that converts objects is valueOf, which represents the original valueOf the object. The default valueOf method returns the object itself. Arrays, functions, and regulars simply inherit from the default method and return the object itself. An exception is the date, which returns a representation of its contents: the number of milliseconds since January 1, 1970.

    let date = new Date(2020, 8, 27);
    console.log(date.valueOf()) // 1601136000000
    Copy the code
  3. ToPrimitive

    ToPrimitive(input[, PreferredType]) The first parameter is input, indicating the input value to be processed. The second parameter is PreferredType, which is optional and indicates the type to which you want to convert, including Number or String.

    PreferredType = String if input is a PreferredType, and Number if input is not a PreferredType. If the input is Undefined, Null, Boolean, Number, or String, return the value.

If it is ToPrimitive(obj, Number), perform the following steps:

If obj is a primitive type, return it directly. Otherwise, call the valueOf method. If obj returns a primitive value, JavaScript returns it. Otherwise, the toString method is called, and if it returns a raw value, JavaScript returns it. Otherwise, JavaScript throws a type error exception.

If ToPrimitive(obj, String) is used, the procedure is as follows:

If obj is a primitive type, return it directly. Otherwise, call the toString method. If obj returns a primitive value, JavaScript returns it. Otherwise, the valueOf method is called, and if it returns a raw value, JavaScript returns it. Otherwise, JavaScript throws a type error exception.

const toPrimitive = (obj, preferredType='Number') => { let Utils = { typeOf: function(obj) { return Object.prototype.toString.call(obj).slice(8, -1); }, isPrimitive: function(obj) { let types = ['Null', 'String', 'Boolean', 'Undefined', 'Number']; return types.indexOf(this.typeOf(obj)) ! = = 1; }}; if (Utils.isPrimitive(obj)) { return obj; } preferredType = (preferredType === 'String' || Utils.typeOf(obj) === 'Date') ? 'String' : 'Number'; if (preferredType === 'Number') { if (Utils.isPrimitive(obj.valueOf())) { return obj.valueOf() }; if (Utils.isPrimitive(obj.toString())) { return obj.toString() }; } else { if (Utils.isPrimitive(obj.toString())) { return obj.toString() }; if (Utils.isPrimitive(obj.valueOf())) { return obj.valueOf() }; } } var a={}; ToPrimitive(a); //"[object object]"Copy the code

Learn from Yuba big guy

Implicit type conversion

Unary operators (-,+)

He can only convert hexadecimal and decimal correctly, but not octal strings.

The console. The log (+ 0 "xa") / / 10. The console log (+ "071") / / 71 console. The log (010) / / 8. You can still convert octal numbers, but you can't convert octal stringsCopy the code

Comparison (= =)

If two values are a number and a string/Boolean, they are actually converted to a number and compared.

ToNumber(x) ==ToNumber(y); ToNumber(x) ==ToNumber(y); ToNumber(x) ==ToNumber(y);Copy the code

When one side is a Boolean value, the Boolean value is converted. Because of this feature, use xx == true and xx == false as little as possible.

Such as:

/ / do not recommend the if (a = = true) {} / / suggest the if (a) {} / / better if (!!!!! a) {}Copy the code
console.log(false == undefined)//false
Copy the code

False == undefined is equivalent to 0 == undefined

console.log(false == [])//true
Copy the code

False == [] equals 0 == [] equals 0 == “” equals 0 == 0, returns true

console.log([] == ! [])//trueCopy the code

First it will be executed! The [] operation, converted to false, is equivalent to [] == false is equivalent to [] == 0 is equivalent to ” == 0 is equivalent to 0 == 0. The result returns true

Question: why! [] indicates false.

Answer: Github, [] represents objects, objects are true, so! [] to false.

Finally, a few more examples of potholes:

console.log(false == "0")
console.log(false == 0)
console.log(false == "")
​
console.log("" == 0)
console.log("" == [])
​
console.log([] == 0)
​
console.log("" == [null])
console.log(0 == "\n")
console.log([] == 0)
Copy the code

All of the above returns true.

[[] [0] + []] [0] [5] + [[], [[[] [0] + []] [0] [4] + [[] [0] + []] [0] [5] + [[] [0] + []] [0] [1] + [[] [0] + []] [0] [2]] + []] [0] [8] + [[[] = = []] of [0] + []] [0] [2] + [[], [[[] [0] + []] [0] [4] + [[] [0] + []] [0] [5] + [[] [0] + []] [0] [1] + [[] [0] + []] [0] [2]] + []] [0] [6] + [[], [[[] [0] + []] [0] [4] + [[] [0] + []] [0] [5] + [[] [0] + []] [0] [1] + [[] [0] + []] [0] [2]] + []] [0] [23] + [[0] + [] []] [0] [3] + [[], [[[] [0] + []] [0] [4] + [[] [0] + []] [0] [5] + [[] [0] + []] [0] [1] + [[] [0] + []] [0] [2]] + []] [0] [8] + [+ (1 + [[0] + [] []] [0] [3] + 309] [0] + []] of [0] [7] + [[], [[[] [0] + []] [0] [4] + [[] [0] + []] [0] [5] + [[] [0] + []] [0] [1] + [[] [0] + []] [0] [2]] + []][0][6]+[[][0] + []][0][0] //i love youCopy the code

Conclusion:

1. Null == null; And both of them are false when compared to all the other values. 2. String == Boolean: convert both operands to Number at the same time. 3. String/Boolean == Number, String/Boolean needs to be converted to Number. 4. Object == Primitive, as opposed to Primitive(using the valueOf and toString methods).Copy the code

When we use a function such as Number(), it is an explicit conversion. The conversion rule is that when the type is a basic type, it is converted by referring to the corresponding table in the specification. When the type is not a basic type, it is converted by referring to the ToPrimitive method in the specification first, and then by referring to the corresponding table. When ToPrimitive is executed, it determines whether the valueOf method or the toString method of the object is executed first.

The judgment of strong and weak types

According to the design way of computer language type system, it can be divided into strong type and weak type. The difference between the two is whether users can be transparently and implicitly converted between different types when calculating. From the user’s point of view, if a language can be implicitly converted all of its type, then its variables, expressions, such as when in operation, even if the type is not correct, also can get correctly by implicit conversion type, this for users, like all types are able to do operation, so the language is called the weak type. In contrast, strongly typed languages do not necessarily have implicit conversions between types.

Js automatically inserts the semicolon rule

First of all, these rules are based on two things:

  1. On the basis of exchange behavior;
  2. The parser tries to merge the new line into the current line, treating the new line as a separate statement if and only if it conforms to THE ASI rules.

ASI rules

1. The merge of the new line into the current line constitutes an illegal statement, and the semicolon is automatically inserted.

If (1 < 10) a = 1 console.log(a); if(1 < 10) a = 1; console.log(a);Copy the code

2. In the continue, return, break, automatically inserted into the semicolon after the throw.

3. The ++, — postfix expression starts a new line and automatically inserts a semicolon at the beginning of the line.

4. The last statement of the block automatically inserts a semicolon.

No ASI rule:

1. A new line begins with (

2. A new line begins with [

3. New lines start with /

4. New lines start with +, -, % and *

5. New lines begin with, or

Two interpretations of {}

(1) when {} is preceded by an operation symbol, +, -, *, /,(), etc., {} is resolved as an object literal. When {} is not preceded by an operator but is; Insert a semicolon (;) after {} at the end, or the browser’s automatic semicolon insertion mechanism. {} is parsed into code blocks. If {} is not preceded by any operator, {} is not followed by a semicolon (;). At the end, Firefox consistently parses to code blocks, while Chrome, slightly differently, parses to object literals.

Operator precedence

Priority of ECMAScript operators

The operator describe
. [] () Field access, array subscripts, function calls, and expression grouping
++ — – + ~! delete new typeof void Unary operator, return data type, object creation, undefined value
* / % Multiplication, division, modulus
+ – + Addition, subtraction, concatenation of strings
<< >> >>> shift
< <= > >= instanceof Less than, less than or equal to, greater than, greater than or equal to, instanceof
= =! = = = =! = = Equal, not equal, strictly equal, not strictly equal
& Bitwise and
^ The bitwise exclusive or
&& Logic and
? : conditions
= oP= Assignment, operation assignment
. Multiple evaluation

References, not values, are incremented.

Let a = [] ++a//1Copy the code

Analyze some examples

1. {} + {} / / chrome: "[object object] [object object]", Firfox: NaN 2. {} + [] / / 0 3. [] + {} / / "[object object]"Copy the code

The contradiction is whether {} is considered a block of code or an object literal.

Firefox is consistent. The first {} is always parsed as a code block, and the {} following the operation symbol is always parsed as an object literal.

But Google sometimes parses it into code blocks and sometimes into object literals. But remember that Chrome automatically adds parentheses to expressions that start with “{” and end with “}”.

Learn from ++[[]][+[]]+[+[]]==10? Implicit conversions for weak JS types

Why are object values on the heap and basic data types on the stack?

Energy is balanced, nothing more than time for space, space for time, the heap is bigger than the stack, the stack is faster than the heap operation, the object is a complex structure, and can be freely expanded, such as: array can be infinite expansion, the object can be free to add attributes. They are placed in the heap in order not to affect stack efficiency. Instead, it finds the actual object in the heap by reference and operates on it. Simple data types are relatively stable and take up very little memory compared to simple data types. Don’t put simple data types in the heap because it takes time to find the actual object by referring to it in the heap, and the combined cost is much greater than the cost of getting the actual value directly from the stack. So values for simple data types are stored directly on the stack.

Shake and throttling

The second parameter apply can be either an array or an array of classes.

Function anti – shaking:

  • Merge several operations into a single operation. The idea is to maintain a timer that fires after a delay, but if it fires again during a delay, the previous timer will be canceled and reset. This way, only the last operation can be triggered.
Function debounce(fn, delay=1000) {// 4, create a flag to store the timer return value let timeout = null; Return function() {return function() {return function(); clearTimeout(); clearTimeout(); // 6, then create a new setTimeout. // This will ensure that the interval after the button is clicked // if the user still clicks the button, Timeout = setTimeout(() => {fn. Apply (this, arguments); }, delay); }; }Copy the code

Function throttling:

  • Causes the function to fire only once in a given period of time. The principle is to trigger the function by determining whether a certain time has been reached.
// 2, function throttle(fn, delay=1000) {// 4, let canRun = true; Return function() {return function() {// 5, check whether the flag is true at the start of the function. canRun) { return; } // 6, set canRun to false to prevent it from being executed before canRun = false; // 7, timer setTimeout(() => {fn.apply(this, arguments); // 8, after executing the event (e.g. calling the interface), set this flag to true canRun = true; }, delay); }; }Copy the code

The difference between:

  • Function throttling guarantees that the actual event handler is executed once in a given time, no matter how often the event is fired, whereas function stabilization only fires once after the last event. For example, in an infinite page load scenario, we want the user to make Ajax requests every once in a while while scrolling the page, rather than requesting data when the user has stopped scrolling. In such a scenario, throttling technology is suitable for implementation.
  • The main is to return function processing. Throttling is set to false. If the execution reaches the specified time. Set the switch to true. Use the if function to determine the switch. Returns a null value. It’s not allowed to continue. Wait until the specified time will continue to execute. Shockproof is to clear the delayer and then set the delayer to call the function.

The browser’s caching mechanism

The biggest and most fundamental difference between a strong cache and a negotiated cache is: The negotiation cache will send the request to the server. The negotiation cache will verify whether the resource matches the negotiation cache by using the header field of the request. If the negotiation cache matches, the server will return the request. Instead of returning the entity of the resource, the client is notified that the resource can be loaded from the cache (304 not modified).

After the browser sends the request to the server for the first time and receives the request result, it determines whether to cache the request result according to the cache identifier of the HTTP header in the response packet. If yes, the request result and the cache identifier are stored in the browser cache.

  • Each time the browser initiates a request, it first looks in the browser cache for the result of the request and the cache identifier
  • Each time the browser gets a returned request result, it stores the result and the cache identifier in the browser cache to update the last cached result.

Limit cache time

  • Cache-control: max-age, in seconds. Is a relative time that expresses how many seconds since the last request for the correct resource the cache is valid.
  • Expires is an absolute time. To indicate that a request made before this point in time can read data directly from the browser without having to make the request
  • Cache-control has a higher priority than Expires. The former is designed to solve the problem that Expires is manually changed at browser time, causing cache errors. If both exist, cache-control is used.

Cache-control has a higher priority than Expires

Mandatory cache

Forcing caching is the process of looking up the request result from the browser cache and deciding whether to use the cached result based on the caching rules for the result.

The cache ends up in two locations. Use size as the identifier.

  • From memory cache indicates that the cache in memory is used. From disk cache indicates that the cache in hard disk is used. The browser reads the cache in memory – > disk order.

In the browser, files such as JS and images are directly stored in the memory cache after being parsed, so you only need to read from the memory cache when refreshing the page. CSS files, on the other hand, are stored in hard disk files, so every time a page is rendered, it must be read from the disk cache.

Negotiate the cache

Negotiated cache is a process in which the browser sends a request to the server with the cache identifier after the mandatory cache is invalid, and the server decides whether to use the cache according to the cache identifier.

The id of the negotiation cache is also returned to the browser in the HTTP header of the response packet along with the request result. The following fields control the negotiation cache: Last-modified/if-modified-since and Etag/if-none-match. Etag/if-none-match has a higher priority than last-modified/if-modified-since.

  • Last-modified is when the server responds to the request by returning the Last time the resource file was Modified on the server.
  • Last-modified/if-modified-since indicates the time when the server’s resources were Last Modified;
  • Etag/ if-none-match indicates the unique identifier of the server resource. Whenever the resource changes, the Etag is regenerated.

If-modified-since is a field that tells the server when the resource was Last Modified when the client made the request again with the last-modified value returned from the Last request. When the server receives the request and finds that the request header contains the if-modified-since field, the server will compare the value of the if-Modified-since field to the last time the resource was Modified on the server. If the last time the resource was Modified on the server is longer than the value of the IF-Modified-since field, the server will compare the value of the if-Modified-since field to the last time the resource was Modified on the server. Then the resource is returned with the status code 200. Otherwise, 304 is returned, indicating that the resource has not been updated and the cache file can continue to be used. (If the last modification time returned from the server is less than the time it took the server to modify the file, the file will be rerequested.)

  • An Etag is a unique identifier (generated by the server) that the server returns in response to a request for the current resource file. This solves the problem of last-modified time accuracy and accuracy

If-none-match indicates that when the client initiates the request again, it carries the unique IDENTIFIER value returned in the last request. The value of this field tells the server the unique identifier value returned in the last request. After receiving the request, the server finds that the request header contains if-none-match, and compares the value of if-None-match with the Etag value of the resource on the server. If the value is consistent, 304 is returned, indicating that the resource is not updated and the cache file continues to be used. If no, the resource file is returned with the status code 200.

Note: the priority of Etag/if-none-match is higher than that of last-modified/if-modified-since. If both Etag/if-none-match exist, only Etag/if-none-match takes effect.

Summary: Force cache takes precedence over negotiation cache. If the cache negotiation succeeds, the status code 304 is returned and the cache continues to be used. Otherwise, the 200 status code is returned and the resource is requested again. HTTP caches can be divided into forced caches and negotiated caches. Forced caches use the browser cache directly within the cache’s lifetime. To negotiate the cache, you need to check whether the server resource has changed. If the server resource has not changed, you can use the browser cache.

An array of random sequence

// Method 1. Function mixArr(arr) {return arr.sort(() => math.random () -0.5)} Function shunflee(arr) {const len = arr.length while(len > 1) {const index = parsetInt(math.random () * len--) [arr[index], arr[len]] = [arr[len], arr[index]] } return arr }Copy the code

Array to heavy

// Method 1. Function removeDup(arr) {const obj = {} arr.foreach ((item) => {if(! Return object.keys (obj [item]) {obj[item] = true}}) return object.keys (obj)} Array. From (new set (arr)) [...new set (arr)]Copy the code

The new operator is used and implemented

1. Create an object

2. The binding of the prototype

3. Change the direction of this

4. Return the new object

function MyNew(Constructor, ... Const newObj = {} // 2. __proto__ = Constructor. Prototype // 3. Change this to refer to Constructor. Call (newObj,... Arg) // 4. Return newObj}Copy the code

The array sort method

Let arr = [1, 22, 11, 332, 42, 12, 222] arr.sort((a, b) => { The second argument is the first entry in the array // console.log(a, b) // Ascending is subtraction, descending is reverse subtraction. Return a - b}) // The array is changed. console.log(arr)Copy the code

String deduplication

const str = [...new Set("zhhsajwnns")].join("");
Copy the code

closure

An internal function that references a variable from an external function is placed in a separate live object. If the external object executes, its memory is freed, but the live object that the internal function referenced is not freed.

advantages

  1. Variables in the scope of an external function can be accessed from an internal function, and the accessed variables reside permanently in memory for later use
  2. Avoid variables that pollute the whole
  3. Store variables as private members in a separate scope

disadvantages

  1. Has a negative impact on memory consumption. Because internal functions store references to external variables, they cannot be garbage collected and increase memory usage. Improper use of internal functions may cause memory leaks
  2. Has a negative impact on processing speed. The level of closure determines the length of the scope chain through which the referenced external variable is found
  3. You can get unexpected values.

Class arrays and arrays

Convert: The length of the converted array is determined by length. When the index is discontinuous, the conversion result is continuous and will be automatically compensated.

  1. Array.from() : discontinuous index. Set to undefined

    / / the code examples let al2 = {length: 4, '1', 1, '0' : 0, a: 'a', 1, 1}; console.log(Array.from(al2)); // [0, 1, undefined, undefined]Copy the code
  2. Array. The prototype. Slice. The call () : the generated is sparse Array.

    / / the code examples let al2 = {length: 4, '1', 1, '0' : 0, a: 'a', 1, 1}; console.log(Array.prototype.slice.call(al2)); //[0, 1, empty × 2]Copy the code

A memory leak

  1. Improper use of closures causes memory leaks
  2. Global variables (js does not actively collect garbage from global variables)
  3. Detached DOM nodes
  4. Console printing
  5. Forgetting timer

Execution context object

The variable object

The scope chain

this

Judge the direction of this by the underlying thought

1. Evaluate MemberExpression (the left part of function parenthesis) and assign the result to ref

2. Check whether ref is a Reference type

The composition of Reference consists of three parts:

  • Base value: The object or EnvironmentRecord in which the property resides
  • Referenced name: variable name
  • strict reference

If IsPropertyReference(ref) is true, then this is GetBase(ref)

  • IsPropertyReference Check whether the value is true. If base value is an object, it is true.

If ref is not Reference, then the value of this is undefined. In strict mode, it is window in non-strict mode