目錄
注意作用域
選擇正確方法
最小化语句数
优化DOM交互
首頁 web前端 js教程 詳細介紹JavaScript最佳實務 –效能

詳細介紹JavaScript最佳實務 –效能

Mar 07, 2017 pm 03:04 PM

注意作用域

避免全域查找

一個例子:

function updateUI(){
    var imgs = document.getElementByTagName("img");
    for(var i=0, len=imgs.length; i<len; i++){
        imgs[i].title = document.title + " image " + i;
    }
    var msg = document.getElementById("msg");
    msg.innnerHTML = "Update complete.";
}
登入後複製

該函數可能看起來完全正常,但它包含了三個對於全域document物件的引用。如果在頁面上有多個圖片,那麼for迴圈中的document引用就會被執行多次甚至上百次,每次都會要進行作用域鏈查找。透過建立一個指向document物件的局部變量,就可以透過限制一次全域查找來改進這個函數的效能:

function updateUI(){
    var doc = document;
    var imgs = doc.getElementByTagName("img");
    for(var i=0, len=imgs.length; i<len; i++){
        imgs[i].title = doc.title + " image " + i;
    }
    var msg = doc.getElementById("msg");
    msg.innnerHTML = "Update complete.";
}
登入後複製

這裡,首先將document物件存在本地的doc變數中;然後在餘下的程式碼中替換原來的document。與原來的版本相比,現在的函數只有一次全域查找,肯定更快。

選擇正確方法

1.避免不必要的屬性尋找

#取得常數值是非常有效率的過程

var value = 5;
var sum = 10 + value;
alert(sum);
登入後複製

程式碼進行了四次常數值查找:數字5,變數value,數字10和變數sum。

在JavaScript中存取陣列元素和簡單的變數尋找效率一樣。所以以下程式碼和前面的範例效率一樣:

var value = [5,10];
var sum = value[0] + value[1];
alert(sum);
登入後複製

物件上的任何屬性查找都比存取變數或陣列花費更長時間,因為必須在原型鏈中對擁有該名稱的屬性進行一次搜素。 屬性查找越多,執行時間越長。

var values = {first: 5, second: 10};
var sum = values.first + values.second;
alert(sum);
登入後複製

這段程式碼使用兩次屬性尋找來計算sum的值。進行一兩次屬性查找並不會導致顯著的效能問題,但是進行數百次則肯定會減慢執行速度。

注意取得單一值的多重屬性查找。例如:

var query = window.location.href.substring(window.location.href.indexOf("?"));
登入後複製

在這段程式碼中,有6次屬性尋找:window.location.href.substring()有3次,window.location.href.indexOf()又有3次。 只要數一數程式碼中的點的數量,就可以確定查找的次數了。 這段程式碼因為兩次用到了window.location.href,同樣的查找進行了兩次,因此效率特別不好。

一旦多次使用到物件屬性,應該將其儲存在局部變數中。之前的程式碼可以如下重寫:

var url = window.locaiton.href;
var query = url.substring(url.indexOf("?"));
登入後複製

這個版本的程式碼只有4次屬性查找,相對於原始版本節省了33%。

一般來講,只要能減少演算法的複雜度,就要盡量減少。盡可能多地使用局部變數將屬性查找替換為值查找,進一步獎,如果即可以用數字化的數組位置進行訪問,也可以使用命名屬性(諸如NodeList物件),那麼使用數字位置。

2.最佳化循環

一個循環的基本最佳化步驟如下所示。

(1)減損迭代-大多數迴圈使用一個從0開始、增加到某個特定值的迭代器。在很多情況下,從最大值開始,在循環中不斷減值的迭代器更有效率。

(2)簡化終止條件-由於每次迴圈過程都會計算終止條件,所以必須保證它盡可能快。也就是說避免屬性查找或其他操作。

(3)簡化迴圈體-迴圈是執行最多的,所以要確保其最大限度地最佳化,確保其他某些可以被輕易移除迴圈的密集計算。

(4使用後測試循環-最常用for迴圈和while迴圈都是前測迴圈。而如do-while這種後測迴圈,可以避免最初終止條件的計算,因此運行更快。 :

for(var i=0; i < value.length; i++){
    process(values[i]);
}
登入後複製

終止條件從value.length簡化成了0。將終止條件和自減運算子組合成了單一語句,循環部分已經最佳化完全了。多餘的一次迴圈而「前測」迴圈則可以避免。函數呼叫往往比較快。

如果循環中的迭代次數不能事先確定,那可以考慮使用一種叫做Duff裝置的技術。

Andrew B.King提出了一個更快的Duff裝置技術,將do-while循環分成2個單獨的循環。計算部分不會在實際迴圈中處理,而是在一個初始化迴圈中進行除以8的運算。 ##針對大數據集使用展開循環可以節省很多時間,但對於小數據集,額外的開銷則可能得不償失。來說不值得。

4.避免双重解释

当JavaScript代码想解析KavaScript的时候就会存在双重解释惩罚。当使用eval()函数或者是Function构造函数以及使用setTimeout()传一个字符串参数时都会发生这种情况。

//某些代码求值——避免!!
eval("alert(&#39;Hello world!&#39;)");

//创建新函数——避免!!
var sayHi = new Function("alert(&#39;Hello world!&#39;)");

//设置超时——避免!!
setTimeout("alert(&#39;Hello world!&#39;)", 500);
登入後複製

在以上这些例子中,都要解析包含了JavaScript代码的字符串。这个操作是不能在初始的解析过程中完成的,因为代码是包含在字符串中的,也就是说在JavaScript代码运行的同时必须新启动一个解析器来解析新的代码。实例化一个新的解析器有不容忽视的开销,所以这种代码要比直接解析慢得多。

//已修正
alert(&#39;Hello world!&#39;);

//创建新函数——已修正
var sayHi = function(){
    alert(&#39;Hello world!&#39;);
};

//设置一个超时——已修正
setTimeout(function(){
    alert(&#39;Hello world!&#39;);
}, 500);
登入後複製

如果要提高代码性能,尽可能避免出现需要按照JavaScript解析的字符串。

5.性能的其他注意事项

(1)原生方法较快

(2)Switch语句较快

(3)位运算符较快

最小化语句数

1.多个变量声明

//4个语句——很浪费
var count = 5;
var color = "blue";
var values = [1,2,3];
var now = new Date();

//一个语句
var count = 5,
    color = "blue",
    values = [1,2,3],
    now = new Date();
登入後複製

2.插入迭代值

当使用迭代值的时候,尽可能合并语句。

var name = values[i];
i++;
登入後複製

前面这2句语句各只有一个目的:第一个从values数组中获取值,然后存储在name中;第二个给变量i增加1.这两句可以通过迭代值插入第一个语句组合成一个语句。

var name = values[i++];
登入後複製

3.使用数组和对象字面量

//用4个语句创建和初始化数组——浪费
var values = new Array();
values[0] = 123;
values[1] = 456;
values[2] = 789;

//用4个语句创建和初始化对象——浪费
var person = new Object();
person.name = "Nicholas";
person.age = 29;
person.sayName = function(){
    alert(this.name);
};
登入後複製

这段代码中,只创建和初始化了一个数组和一个对象。各用了4个语句:一个调用构造函数,其他3个分配数据。其实可以很容易地转换成使用字面量的形式。

//只有一条语句创建和初始化数组
var values = [13,456,789];

//只有一条语句创建和初始化对象
var person = {
    name : "Nicholas",
    age : 29,
    sayName : function(){
        alert(this.name);
    }
};
登入後複製

重写后的代码只包含两条语句,减少了75%的语句量,在包含成千上万行JavaScript的代码库中,这些优化的价值更大。
只要有可能,尽量使用数组和对象的字面量表达方式来消除不必要的语句。

优化DOM交互

1.最小化现场更新

一旦你需要访问的DOM部分是已经显示的页面的一部分,那么你就是在进行一个现场更新。现场更新进行得越多,代码完成执行所花的事件就越长。

var list = document.getElementById(&#39;myList&#39;),
    item,
    i;
for (var i = 0; i < 10; i++) {
    item = document.createElement("li");
    list.appendChild(item);
    item.appendChild(document.createTextNode("Item" + i));
}
登入後複製

这段代码为列表添加了10个项目。添加每个项目时,都有2个现场更新:一个添加li元素,另一个给它添加文本节点。这样添加10个项目,这个操作总共要完成20个现场更新。

var list = document.getElementById(&#39;myList&#39;),
    fragment = document.createDocumentFragment(),
    item,
    i;
for (var i = 0; i < 10; i++) {
    item = document.createElement("li");
    fragment.appendChild(item);
    item.appendChild(document.createTextNode("Item" + i));
}
list.appendChild(fragment);
登入後複製

在这个例子中只有一次现场更新,它发生在所有项目都创建好之后。文档片段用作一个临时的占位符,放置新创建的项目。当给appendChild()传入文档片段时,只有片段中的子节点被添加到目标,片段本身不会被添加的。

一旦需要更新DOM,请考虑使用文档片段来构建DOM结构,然后再将其添加到现存的文档中。

2.使用innerHTML

有两种在页面上创建DOM节点的方法:使用诸如createElement()和appendChild()之类的DOM方法,以及使用innerHTML。对于小的DOM更改而言,两种方法效率都差不多。然而,对于大的DOM更改,使用innerHTML要比使用标准DOM方法创建同样的DOM结构快得多。

当把innerHTML设置为某个值时,后台会创建一个HTML解析器,然后使用内部的DOM调用来创建DOM结构,而非基于JavaScript的DOM调用。由于内部方法是编译好的而非解释执行的,所以执行快得多。

var list = document.getElementById("myList");
    html = "";
    i;

for (i=0; i < 10; i++){
    html += "<li>Item " + i +"</li>";
}
list.innerHTML = html;
登入後複製

使用innerHTML的关键在于(和其他的DOM操作一样)最小化调用它的次数。

var list = document.getElementById("myList");
    i;

for (i=0; i < 10; i++){
    list.innerHTML += "<li>Item " + i +"</li>";  //避免!!!
}
登入後複製

这段代码的问题在于每次循环都要调用innerHTML,这是极其低效的。调用innerHTML实际上就是一次现场更新。构建好一个字符串然后一次性调用innerHTML要比调用innerHTML多次快得多。

3.使用事件代理(根据第13章的概念,我认为此处应为“事件委托”更为妥当)

4.注意HTMLCollection

任何时候要访问HTMLCollection,不管它是一个属性还是一个方法,都是在文档上进行一个查询,这个查询开销很昂贵。

var images = document.getElementsByTagName("img"),
    image,
    i,len;

for (i=0, len=images.length; i < len; i++){
    image = images[i];
    //处理
}
登入後複製

将length和当前引用的images[i]存入变量,这样就可以最小化对他们的访问。发生以下情况时会返回HTMLCollection对象:

  • 进行了对getElementsByTagName()的调用;

  • 获取了元素的childNodes属性;

  • 获取了元素的attributes属性;

  • 访问了特殊的集合,如document.forms、document.images等。

 以上就是详细介绍JavaScript最佳实践 –性能的内容,更多相关内容请关注PHP中文网(www.php.cn)!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
2 週前 By 尊渡假赌尊渡假赌尊渡假赌
倉庫:如何復興隊友
4 週前 By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒險:如何獲得巨型種子
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

Win11和Win10系統效能對比,究竟哪一個更勝一籌? Win11和Win10系統效能對比,究竟哪一個更勝一籌? Mar 27, 2024 pm 05:09 PM

Win11和Win10系統效能對比,究竟哪一個更勝一籌?

Windows10與Windows11效能比較:哪個更勝一籌? Windows10與Windows11效能比較:哪個更勝一籌? Mar 28, 2024 am 09:00 AM

Windows10與Windows11效能比較:哪個更勝一籌?

麒麟8000處理器抗衡驍龍系列:誰能稱王? 麒麟8000處理器抗衡驍龍系列:誰能稱王? Mar 25, 2024 am 09:03 AM

麒麟8000處理器抗衡驍龍系列:誰能稱王?

本地運作效能超越 OpenAI Text-Embedding-Ada-002 的 Embedding 服務,太方便了! 本地運作效能超越 OpenAI Text-Embedding-Ada-002 的 Embedding 服務,太方便了! Apr 15, 2024 am 09:01 AM

本地運作效能超越 OpenAI Text-Embedding-Ada-002 的 Embedding 服務,太方便了!

PHP與Go語言比較:效能差異大 PHP與Go語言比較:效能差異大 Mar 26, 2024 am 10:48 AM

PHP與Go語言比較:效能差異大

不同Java框架的效能對比 不同Java框架的效能對比 Jun 05, 2024 pm 07:14 PM

不同Java框架的效能對比

PHP 陣列鍵值翻轉:不同方法的效能比較分析 PHP 陣列鍵值翻轉:不同方法的效能比較分析 May 03, 2024 pm 09:03 PM

PHP 陣列鍵值翻轉:不同方法的效能比較分析

C++ 函式對程式效能有哪些影響? C++ 函式對程式效能有哪些影響? Apr 12, 2024 am 09:39 AM

C++ 函式對程式效能有哪些影響?

See all articles