Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to write high-performance JavaScript code

2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article mainly introduces "how to write high-performance JavaScript code". In daily operation, I believe many people have doubts about how to write high-performance JavaScript code. The editor consulted all kinds of materials and sorted out simple and easy-to-use methods of operation. I hope it will be helpful to answer the doubts of "how to write high-performance JavaScript code". Next, please follow the editor to study!

How does JavaScript work in V8?

If you don't have a deep understanding of JS engines, there's nothing wrong with developing a large Web app, just like a driver who can only look at the hood and not the engine inside the hood. Since Chrome is my browser's first choice, let's talk about its JavaScript engine. V8 consists of the following core parts:

A basic compiler that parses JavaScript code and generates native machine code before code execution, rather than executing bytecode or simply interpreting it. The code was not highly optimized at first.

V8 builds objects as object models. Objects are represented as associative arrays in JavaScript, but in V8 objects are seen as hidden classes, an internal type system for optimizing queries.

The runtime analyzer monitors the running system and identifies functions of "hot" (such as code that takes a long time to run).

The optimization compiler recompiles and optimizes code identified by the runtime parser as "hot" and makes optimizations such as "inline" (such as replacing the location of the function call with the body of the callee).

V8 supports de-optimization, which means that if the optimization compiler finds that the assumption of code optimization is too optimistic, it will discard the optimized code.

V8 has a garbage collector, and understanding how it works is as important as optimizing JavaScript.

Garbage collection

Garbage collection is a form of memory management, which is actually the concept of a collector that attempts to reclaim memory occupied by objects that are no longer in use. In a garbage collection language like JavaScript, objects that are still being referenced in the application are not cleared.

Manually eliminating object references is not necessary in most cases. Everything will work fine by simply putting variables where they are needed (ideally, as much as possible in local scope, that is, in the function in which they are used rather than in the outer layer of the function).

The garbage collector attempts to reclaim memory.

In JavaScript, it is impossible to enforce garbage collection. You shouldn't do this because the garbage collection process is controlled by the runtime, which knows what is the best time to clean up.

"eliminate the misunderstanding of citation"

There is a lot of discussion about JavaScript memory collection on the Internet about the keyword delete. Although it can be used to remove key from an object (map), some developers think it can be used to force "de-referencing." It is recommended that you avoid using delete as much as possible. In the following example, delete o.x does more harm than good because it changes the hidden class of o and makes it a "slow object".

Var o = {x: 1}; delete o.x; / / true o.x; / / undefined

You can easily find reference deletions in the popular JS library-this is language-specific. One thing to note here is to avoid modifying the structure of the "hot" object at run time. The JavaScript engine can detect such "hot" objects and try to optimize them. If the structure of the object does not change significantly during the life cycle, the engine will be easier to optimize the object, and the delete operation will actually trigger this large structural change, which is not conducive to engine optimization.

There are also misunderstandings about how null works. Setting an object reference to null does not make the object "empty", just sets its reference to null. Using o.x = null is better than using delete, but it may not be necessary.

Var o = {x: 1}; o = null; o; / / null o.x / / TypeError

If this reference is the last reference to the current object, the object will be garbage collected. If this reference is not the last reference to the current object, the object is accessible and will not be garbage collected.

It is also important to note that global variables are not cleaned up by the garbage collector during the lifetime of the page. Variables in the global object scope of the JavaScript runtime persist no matter how long the page is open.

Var myGlobalNamespace = {}

Global objects are cleaned only when the page is refreshed, navigated to another page, closed tabs, or exited from the browser. Variables in the scope of the function will be cleaned up when they are out of scope, that is, when the function is exited without any references, and such variables will be cleaned up.

Rule of thumb

In order for the garbage collector to collect as many objects as possible as soon as possible, do not hold objects that are no longer in use. Here are a few things to keep in mind:

As mentioned earlier, using variables within the appropriate scope is a better option to manually eliminate references. That is, a variable is only used in a function scope, so do not declare it in the global scope. This means cleaner and more worry-free code.

Be sure to unbind event listeners that are no longer needed, especially those bound to DOM objects that are about to be destroyed.

If the data you are using is cached locally, be sure to clean up the cache or use the aging mechanism to avoid storing a large amount of data that is not reused.

Function

Next, let's talk about functions. As we have already said, garbage collection works by collecting blocks of memory (objects) that are no longer accessed. To better illustrate this point, here are some examples.

Function foo () {var bar = new LargeObject (); bar.someCall ();}

When foo returns, the object pointed to by bar will be automatically reclaimed by the garbage collector because it no longer has any existing references.

Compare this:

Function foo () {var bar = new LargeObject (); bar.someCall (); return bar;} / / somewhere else var b = foo ()

Now we have a reference to the bar object so that the lifetime of the bar object lasts from the call to foo until the caller specifies another variable b (or b is out of range).

Closure (CLOSURES)

When you see a function and return an internal function, the internal function will gain out-of-scope access, even after the external function is executed. This is a basic closure-- an expression of a variable that can be set in a specific context. For example:

Function sum (x) {function sumIt (y) {return x + y;}; return sumIt;} / / Usage var sumA = sum (4); var sumB = sumA (3); console.log (sumB); / / Returns 7

The function object (sumIt) generated in the context of the sum call cannot be recycled, it is referenced by the global variable (sumA), and can be called through sumA (n).

Let's look at another example. Can we access the variable largeStr here?

Var a = function () {var largeStr = new Array (1000000). Join ('x'); return function () {return largeStr;};} ()

Yes, we can access largeStr through a (), so it is not recycled. What about the next one?

Var a = function () {var smallStr = 'x'; var largeStr = new Array (1000000). Join (' x'); return function (n) {return smallStr;};} ()

We can no longer visit largeStr, which is already a candidate for garbage collection. [translator's note: because there are no external references to largeStr]

Timer

One of the worst memory leaks is in a loop, or in setTimeout () / setInterval (), but this is quite common. Consider the following example:

Var myObj = {callMeMaybe: function () {var myRef = this; var val = setTimeout (function () {console.log ('Time is running outposts'); myRef.callMeMaybe ();}, 1000);}}

If we run myObj.callMeMaybe (); to start the timer, we can see that the console prints out "Time is running out!" every second. If you continue to run myObj = null, the timer is still active. For continuous execution, the closure passes the myObj to the setTimeout so that the myObj cannot be recycled. Instead, it refers to myObj because it captures myRef. This is the same as passing closures to other functions in order to keep references.

It is also worth remembering that references in setTimeout/setInterval calls, such as functions, will need to be executed and completed before they can be garbage collected.

Beware of performance traps

Never optimize your code until you really need it. It is now common to see benchmark tests that show that N is more optimized than M in V8, but testing in module code or applications shows that the real effect of these optimizations is much smaller than you expect.

It is better to do nothing than to do too much.

For example, we want to create a module like this:

A local data source is required to contain the digital ID

Draw a table containing this data

Add an event handler to toggle the cell's css class when the user clicks on any cell

There are several different factors in this problem, although it is also easy to solve. How do we store data, how do we draw tables efficiently and append them into DOM, and how do we better handle table events?

The first (naive) way to deal with these problems is to use objects to store data and put it in an array, use jQuery to traverse the data to draw a table and append it to DOM, and finally use events to bind our desired click behavior.

Note: this is not what you should do.

Var moduleA = function () {return {data: dataArrayObject, init: function () {this.addTable (); this.addEvents ();}, addTable: function () {for (var I = 0; I < rows; iTunes +) {$tr = $('') For (var j = 0; j < this.data.length; jacks +) {$tr.append (''+ this.data [j] ['id'] +');} $tr.appendTo ($tbody) }, addEvents: function () {$('table td') .on (' click', function () {$(this) .toggleClass ('active');});}};} ()

This code accomplishes the task simply and effectively.

But in this case, the data we traverse is just the numeric attribute ID that should simply be stored in the array. Interestingly, using DocumentFragment and native DOM methods directly is a better choice than using jQuery (in this way) to generate tables, and of course, event brokers have higher performance than binding each td individually.

Note that although jQuery uses DocumentFragment internally, in our example, the code calls append within the loop and these calls involve some other little knowledge, so it doesn't do much optimization here. Hopefully this won't be a pain point, but be sure to benchmark to make sure you code ok.

For our example, the above approach has resulted in a (desired) performance improvement. The event broker is an improvement on simple binding, and the optional DocumentFragment also helps.

Var moduleD = function () {return {data: dataArray, init: function () {this.addTable (); this.addEvents ();}, addTable: function () {var td, tr; var frag = document.createDocumentFragment (); var frag2 = document.createDocumentFragment () For (var I = 0; I < rows; iTunes +) {tr = document.createElement ('tr'); for (var j = 0; j < this.data.length; jacks +) {td = document.createElement (' td'); td.appendChild (document.createTextNode (this.data [j])) Frag2.appendChild (td);} tr.appendChild (frag2); frag.appendChild (tr);} tbody.appendChild (frag) }, addEvents: function () {$('table') .on (' click', 'td', function () {$(this) .toggleClass (' active');});}};} ()

Next take a look at other ways to improve performance. You may have read somewhere that using prototype mode is better than module mode, or have heard of better performance using JS template frameworks. Sometimes they are, but they are actually used to make the code more readable. By the way, there is also pre-compilation! Let's see how it performs in practice.

ModuleG = function () {}; moduleG.prototype.data = dataArray; moduleG.prototype.init = function () {this.addTable (); this.addEvents ();}; moduleG.prototype.addTable = function () {var template = _ .template ($('# template'). Text ()); var html = template ({'data': this.data}); $tbody.append (html);} ModuleG.prototype.addEvents = function () {$('table') .on (' click', 'td', function () {$(this) .toggleClass (' active');});}; var modG = new moduleG ()

It turns out that the performance improvement in this case is negligible. The choice of templates and prototypes doesn't really provide much more. In other words, performance is not the reason why developers use them, but the readability, inheritance model, and maintainability brought to the code.

More complex issues include efficiently drawing pictures on canvas and manipulating pixel data with or without typed arrays.

Be sure to learn more about the benchmarking of these scenarios before you apply some methods to your own applications. Some people may remember the shoot-off of the JS template and the subsequent extension. You need to make sure that benchmarking does not exist in virtual applications that you can't see, but that you should test the optimizations in your actual code.

V8 optimization skills

The optimization points of each V8 engine are described in detail outside the scope of this article, and of course there are many techniques worth mentioning here. By keeping these tips in mind, you can reduce the number of code that is underperforming.

Specific patterns can get V8 out of the optimization dilemma, such as try-catch. To learn more about which functions can or cannot be optimized, you can use the-trace-opt file.js command in V8's scripting tool D8.

If you care about speed, try to keep your function single, that is, make sure that variables (including properties, arrays, function arguments) use only objects contained in the same hidden class. For example, don't do this:

Function add (x, y) {return Xeroy;} add (1,2); add ('axiombinomy'); add (my_custom_object, undefined)

Do not load uninitialized or deleted elements. It won't go wrong if you do this, but it will slow you down.

Do not make the function body too large, which will make optimization more difficult.

For more information, check out Daniel Clifford's sharing Breaking the JavaScript Speed Limit with V8 on Google I Zero O. The Optimizing For V8-A Series is also well worth reading.

Object VS array: which one should I use?

If you want to store a string of numbers, or some objects of the same type, use an array.

If what you need semantically is a bunch of object attributes (different types), use one object and property. This is very efficient in terms of memory and quite fast.

Elements indexed by integers, whether stored in an array or an object, are much faster than traversing the properties of an object.

The properties of objects are complex: they can be created by setter and have different enumerations and writeability. There is no such customization in the array, but there are only two states: yes and no. At the engine level, this allows for more optimization of the storage structure. Especially when there are numbers in the array, for example, when you need a container, you don't have to define a class with the xrecoery _ z attribute, just use the array.

There is only one major difference between an object and an array in JavaScript, and that is the magic length property of the array. If you maintain this property yourself, objects and arrays in V8 are as fast as they are.

Skills in using objects

Use a constructor to create an object. This ensures that all objects it creates have the same hidden classes and helps avoid changing those classes. As an added benefit, it is also slightly faster than Object.create ()

In your application, there are no restrictions on the use of different types of objects and their complexity (to a reasonable extent: long prototype chains are often harmful, and objects with only a few attributes are faster than large objects). For "hot" objects, try to keep the prototype chain short and have few attributes.

Object cloning

Object cloning is a common problem for application developers. Although various benchmarks can prove that V8 handles this problem well, you still need to be careful. Copying big things is usually slow-don't do that. The for..in loop in JS is particularly bad because it has a demonic specification, and no matter which engine it is, it may never be faster than any object.

When you must copy objects on a critical performance code path, use an array or a custom copy constructor function to explicitly copy each property. This is probably the fastest way:

Function clone (original) {this.foo = original.foo; this.bar = original.bar;} var copy = new clone (original)

Caching function in module mode

Caching functions when using module mode can lead to performance improvements. See the example below, because it always creates a new copy of the member function, and the changes you see may be slow.

Also note that this approach is significantly better, not just relying on the prototype pattern (confirmed by jsPerf tests).

Performance improvement when using module mode or prototype pattern

This is a performance comparison test between prototype pattern and module pattern:

/ / Prototypal pattern Klass1 = function () {} Klass1.prototype.foo = function () {log ('foo');} Klass1.prototype.bar = function () {log (' bar');} / / Module pattern Klass2 = function () {var foo = function () {log ('foo') }, bar = function () {log ('bar');}; return {foo: foo, bar: bar}} / Module pattern with cached functions var FooFunction = function () {log (' foo');}; var BarFunction = function () {log ('bar');} Klass3 = function () {return {foo: FooFunction, bar: BarFunction}} / / Iteration tests / / Prototypal var i = 1000, objs = []; while (iMurt -) {var o = new Klass1 () objs.push (new Klass1 ()); o.bar; o.foo } / / Module pattern var i = 1000, objs = []; while (iMurt -) {var o = Klass2 () objs.push (Klass2 ()); o.bar; o.foo;} / / Module pattern with cached functions var i = 1000, objs = [] While (iMurt -) {var o = Klass3 () objs.push (Klass3 ()); o.bar; o.foo;} / / See the test for full details

Techniques for using arrays

Next, let's talk about array-related techniques. In general, do not delete array elements, which will cause the array to transition to a slower internal representation. When the index becomes sparse, V8 will move the element to a slower dictionary mode.

Array literals

Array literals are useful because they can imply the size and type of the VM array. It is usually used in small arrays.

/ / Here V8 can see that you want a 4-element array containing numbers: var a = [1,2,3,4]; / / Don't do this: a = []; / / Here V8 knows nothing about the array for (var I = 1; I

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report