How to improve the execution efficiency of JS on the data access layer

Source: Internet
Author: User
Tags terminates

This article is about how to improve the efficiency of JS code execution from the data access level. Generally speaking, there are several principles:

    1. Reading and writing local variables is always the fastest in a function, while the reading of global variables is the slowest;
    2. Use the WITH statement as little as possible, as it increases the access cost of data other than the WITH statement;
    3. Closures, while powerful, can not be abused, otherwise it will affect the execution speed and memory;
    4. Nested object members can significantly affect performance and minimize use;
    5. Avoid accessing global variables in object members or functions multiple times, and assign them to local variables to cache as much as possible.

Such a few words seem simple, but to deeply understand the truth of which involves JS identifier parsing, scope chain, run-time context (also known as the execution Environment), prototype chain, closure, and a series of concepts, before I have seen an online translation of JavaScript closures, the article explains these things, But it was indefinitely a few times. However, the book is illustrated, very good understanding, not by the feeling of the cow is a cow ~ Yanshi

Scope chain and Identifier resolution

Each JS function is represented as an object that has an internal property [[Scope]] that contains a collection of objects in the scope of a function being created, which is called the function's scope chain (scope chain), which determines which data can be accessed by the function. Each object in a function scope is called a mutable object (variable. Object). When a function is created, its scope chain is populated with data objects that are accessible in the scope of the creation of this function.

For example, the following global function:

View Source print?
1 functionadd(num1, num2) {
2     varsum = num1 + num2;
3     returnsum;
4 }

Since this function is created under the global scope, only a single Mutable object-Global object is populated in the scope chain of the function add ():

Executing this function creates an internal object, called the runtime context (execution context), which defines the environment at which the function executes. The runtime context of a function is unique each time it executes, so calling the same function multiple times will result in multiple run-time contexts being created. When the function finishes executing, the execution context is destroyed.

And what does the function scope chain we just talked about have to do with this run-time context? So, each run-time context has its own scope chain for identifier parsing, and its scope chain refers to the scope chain that the function's internal object [[Scope]] points to. An "Activation object" is also created, which contains all the local variables, named arguments, parameter sets, and this of the function, which are treated as mutable objects when the function is executed, and before it is one thing. The object is then pushed into the front of the scope chain so that the run-time context is created, and the active object is destroyed when the runtime context is destroyed (except for closures, as described later).

For example, the run-time context and scope chain for the previous add () function run:

When the function executes, every time a variable is encountered, an identifier parsing process is performed, which searches the scope chain from start to finish, from the active object of the currently running function to the global object, until it finds an identifier of the same name, and is considered undefined if it is not found.

Performance of identifier parsing

Identifier parsing is a cost, in the run-time context of the chain, the deeper the location of an identifier, its read and write speed is slower, obviously read and write to the local variable is the fastest, because its object is at the forefront of the chain of action. A good rule of thumb is that if a cross-scope value is referenced more than once in a function, it is stored in a local variable.

In addition, the temporary change of the scope chain during code execution also affects the performance of identifier resolution, with two statements causing the condition--with and catch, when the two statements are executed, a new Mutable object (with the object specified in the statement, catch exception object) is pushed into the scope chain header , so that the original accessible objects are pushed back one level, which makes them more expensive to access. Therefore, it is best to avoid using the WITH statement, and the catch statement can define a function to handle the error to reduce the number of statements within the catch.

is an example of a scope chain that has changed in the WITH statement:

Closures, scopes, and memory

After understanding the scope chain, the closure is understood. Closures are one of the most powerful features of JavaScript, allowing functions to access data outside the local scope. However, there is a performance problem related to closures, consider the following code:

View Source print?
1 functionassignEvents() {
2     varid = ‘xdi9592‘;
3     document.getElementById(‘save-btn‘).onclick = function(event) {
4         saveDocument(id);
5     }
6 }

The OnClick event handler inside the function is a closure, in order to allow the closure to access the data within the function assignevents (), when the closure is created, its [[scope]] property is initialized to the object in the scope chain when its external function runs, that is, the closure [[Scope]] property contains a reference to the same object as the run-time context scope.

Scope chains and closures for the function assignevents () run-time context:

Typically, the active object of a function is destroyed with the run-time context. However, when a closure is introduced, the activation object cannot be destroyed because the reference still exists in the [Scope] property of the closure. This means that the closure in the script requires more memory overhead than the non-closure function.

When a closure is executed, an active object is created for the closure itself and placed at the forefront of the scope chain:

At this point, the cost of access to the outside of the closure data (such as ID, savedocument, and so on) is greater because they are pushed down one level in the scope chain. This is the main performance concern of using closures: you frequently access a large number of cross-scope identifiers, which can result in a performance penalty for each visit.

Object member resolution, prototyping, prototype chain

An object member refers to an object's properties or methods, and when we want to access an object member such as TESTOBJ.ABC, we first find the Testobj object in the process of identifier parsing, and the next step is to access the ABC property (or method) for the object member resolution process.

Objects in JavaScript are prototype-based, and prototypes are the basis for other objects that define and implement a list of members that a new object must contain. The object is bound to its prototype through an internal property __proto__ (which is visible to developers in Firefox, Safari, and Chrome).

An object can have two types of members: instance members and prototype members. Instance members exist in the object instance, and the prototype members are inherited by the object prototype. Once you have created an instance of a built-in object (such as object and array), they automatically have an object instance as the prototype, as in the following code:

View Source print?
1 varbook = {
2     title : ‘High Performance JavaScript‘,
3     publisher : ‘Yahoo! Press‘
4 }
5 alert(book.toString());  //"[object Object]"

In this example, the book does not define the ToString () method, but it can be executed successfully because the method ToString () is a prototype member inherited from the object book:

Note that there are also __proto__ properties in the book prototype, and that the JavaScript object is based on prototypes, since each object has a prototype, which naturally forms a "chain," which we call the prototype chain, and the prototype chain terminates on the object that the prototype is null. The parsing of the object members is actually the traversal of the prototype chain, starting from the instance members to find the prototype members.

You can also define and use a constructor to create another type of prototype, which inserts the newly defined prototype object into the prototype chain, taking the following example:

View Source print?
1 functionBook(title, publisher) {
2     this.title = title;
3     this.publisher = publisher;
4 }
5 Book.prototype.sayTitle = function() {
6     alert(this.title);
7 }
8 varbook1 = newBook(‘High Performance JavaScript‘, ‘Yahoo! Press‘);
9 varbook2 = newBook(‘JavaScript: The Good Parts‘, ‘Yahoo! Press‘);

A prototype is created manually for the book object, and a method Saytitle () is defined, and for instance Book1 the prototype chain is this: Book1 prototype, Book.prototype, Book.prototype prototype Object, prototype of object, NULL.

When you want to access a member of a BOOK1, the process of retrieving its prototype chain is to first find the instance member of Book1 (title, publisher), and then find the prototype member (Saytitle) in Book1 's prototype Book.prototype if not found , if not yet, continue to find the prototype member of the Book.prototype prototype object (ToString, valueOf ...), if it is still not found, continue to find the prototype of object, but the prototype of object is NULL, the lookup terminates, At this point the member is determined to be undefined, and if any of the procedures are found, the lookup is immediately aborted and returned. The relationship of the prototype chain

Performance of Object member resolution

As with identifier parsing, the parsing of object members is also costly, and in the course of the prototype chain, each layer increases the loss of performance, so the deeper the object exists in the prototype chain, the slower it is to find it.

In addition, because an object member may contain other members, such as Window.location.href, each time a dot operator is encountered, the nested member causes the JavaScript engine to search all object members, apparently the deeper the object is nested, the slower the access is, and therefore, the less it is used, For example, executing location.href is always faster than window.location.href.

Obviously, when you want to access object members frequently, it's best to cache them with variables.

How to improve the execution efficiency of JS on the data access layer

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.