JavaScript效能陷阱小結(附執行個體說明)

來源:互聯網
上載者:User

1.避免使用eval或者Function建構函式
2.避免使用with
3.不要在效能要求關鍵的函數中使用try-catch-finally
4.避免使用全域變數
5.避免在效能要求關鍵的函數中使用for-in
6.使用字串累加計算風格
7.原操作會比函數調用快
8.設定setTimeout() 和 setInterval() 時傳遞函數名而不是字串
9.避免在對象中使用不需要的DOM引用
10.最清晰的目標速度,最小化範圍鏈
11.試著在指令碼中少使用注釋,避免使用長變數名
12.在當前範圍儲存應用的外部變數
13.使用變數緩衝值

1.避免使用eval或者Function建構函式
使用eval或者Function建構函式的代價是非常昂貴的,每次都需要指令碼引擎轉換原始碼到可執行代碼。
此外,使用eval處理字串必須在運行時解釋。

運行緩慢的代碼: 複製代碼 代碼如下:function addMethod(object, property, code) {
object[property] = new Function(code);
}
addMethod(myObj, 'methodName', 'this.localVar=foo');

運行更快的代碼: 複製代碼 代碼如下:function addMethod(object, property, func) {
object[property] = func;
}
addMethod(myObj, 'methodName', function () { 'this.localVar=foo'; });

2.避免使用with
儘管很方便,with需要附加的尋找引用時間,因為它在編譯的時候並不知道範圍的上下沒。

運行緩慢的代碼: 複製代碼 代碼如下:with (test.object) {
foo = 'Value of foo property of object';
bar = 'Value of bar property of object';
}

運行更快的代碼: 複製代碼 代碼如下:var myObj = test.object;
myObj.foo = 'Value of foo property of object';
myObj.bar = 'Value of bar property of object';

3.不要在效能要求關鍵的函數中使用try-catch-finally
try-catch-finally在運行時每次都會在當前範圍建立一個新的變數,用於分配語句執行的異常。
異常處理應該在指令碼的高層完成,在異常不是很頻繁發生的地方,比如一個迴圈體的外面。
如果可能,盡量完全避免使用try-catch-finally。

運行緩慢的代碼: 複製代碼 代碼如下:var object = ['foo', 'bar'], i;
for (i = 0; i < object.length; i++) {
try {
// do something that throws an exception
} catch (e) {
// handle exception
}
}

運行更快的代碼: 複製代碼 代碼如下:var object = ['foo', 'bar'], i;
try {
for (i = 0; i < object.length; i++) {
// do something
}
} catch (e) {
// handle exception
}

4.避免使用全域變數
如果你在一個函數或者其它範圍中使用全域變數,指令碼引擎需要遍曆整個範圍去尋找他們。
全域範圍中的變數在指令碼的生命週期裡都存在,然後局部範圍的會在局部範圍失去的時候被銷毀。

運行緩慢的代碼: 複製代碼 代碼如下:var i,
str = '';
function globalScope() {
for (i=0; i < 100; i++) {
str += i; // here we reference i and str in global scope which is slow
}
}
globalScope();

運行更快的代碼: 複製代碼 代碼如下:function localScope() {
var i,
str = '';
for (i=0; i < 100; i++) {
str += i; // i and str in local scope which is faster
}
}
localScope();

5.避免在效能要求關鍵的函數中使用for-in
for-in迴圈需要指令碼引擎建立一張所有可枚舉屬性的列表,並檢查是否與先前的重複。
如果你的for迴圈範圍中的代碼沒有修改數組,可以預先計算好數組的長度用於在for迴圈中迭代數組。

運行緩慢的代碼: 複製代碼 代碼如下:var sum = 0;
for (var i in arr) {
sum += arr[i];
}

運行更快的代碼: 複製代碼 代碼如下:var sum = 0;
for (var i = 0, len = arr.length; i < len; i++) {
sum += arr[i];
}

6.使用字串累加計算風格
使用+運算會在記憶體中建立一個新的字串並把串連的值賦給它。僅僅是將這個結果賦值給一個變數。
為了避免串連結果的中間變數,可以使用+=來直接賦值結果。

運行緩慢的代碼: 複製代碼 代碼如下:a += 'x' + 'y';

運行更快的代碼: 複製代碼 代碼如下:a += 'x'; a += 'y';

7.原操作會比函數調用快
可以考慮在效能要求關鍵的迴圈和函數中使用可以替代的原操作。
運行緩慢的代碼: 複製代碼 代碼如下:var min = Math.min(a, b);
arr.push(val);

運行更快的代碼: 複製代碼 代碼如下:var min = a < b ? a : b;
arr[arr.length] = val;

8.設定setTimeout() 和 setInterval() 時傳遞函數名而不是字串
如果你傳遞一個字串到setTimeout() 或者 setInterval()中,字串將會被eval計算而導致緩慢。
使用一個匿名函數封裝來代替,這樣在編譯的時候就可以被解釋和最佳化。

運行緩慢的代碼:
setInterval('doSomethingPeriodically()', 1000);
setTimeOut('doSomethingAfterFiveSeconds()', 5000);

運行更快的代碼: 複製代碼 代碼如下:setInterval(doSomethingPeriodically, 1000);
setTimeOut(doSomethingAfterFiveSeconds, 5000);

9.避免在對象中使用不需要的DOM引用

不要這麼做:

複製代碼 代碼如下:var car = new Object();
car.color = "red";
car.type = "sedan"

更好的一種形式:

複製代碼 代碼如下:var car = {
color : "red";
type : "sedan"
}

10.最清晰的目標速度,最小化範圍鏈

低效率方法: 複製代碼 代碼如下:var url = location.href;

一種高效形式: 複製代碼 代碼如下:var url = window.location.href;

11.試著在指令碼中少使用注釋,避免使用長變數名
儘可能的保證注釋少或者避免使用注釋,特別是在函數,迴圈以及數組中。
注釋不必要的減緩指令碼執行並且增加了檔案大小。比如:

不建議的形式:

複製代碼 代碼如下:function someFunction()
{
var person_full_name="somename"; /* stores the full name*/
}

更好的寫法: 複製代碼 代碼如下:function someFunction()
{
var name="somename";
}

12.在當前範圍儲存應用的外部變數
當一個函數被執行的運行上下問被穿件,一個活動的對象會包含所有局部變數會被推到上下文鏈的前面。
在範圍鏈中,最慢的是清楚的識別標識符,意味著局部變數是最快的。儲存頻繁使用的外部變數讀和寫都會明顯的加快。這對於全域變數和其他深層次的標識符尋找特別明顯。
同樣,在當前範圍中的變數(var myVar)比對象像屬性的訪問速度快(this.myVar)。

運行緩慢的代碼:

複製代碼 代碼如下:function doSomething(text) {
var divs = document.getElementsByTagName('div'),
text = ['foo', /* ... n ... */, 'bar'];
for (var i = 0, l = divs.length; i < l; i++) {
divs[i].innerHTML = text[i];
}
}

運行更快的代碼:

複製代碼 代碼如下:function doSomethingFaster(text) {
var doc = document,
divs = doc.getElementsByTagName('div'),
text = ['foo', /* ... n ... */, 'bar'];
for (var i = 0, l = divs.length; i < l; i++) {
divs[i].innerHTML = text[i];
}
}

如果你需要訪問一個元素(如 head)在一個大的迴圈中,使用一個本地的DOM訪問(如例子中的get)會更快。
運行更快的代碼: 複製代碼 代碼如下:function doSomethingElseFaster() {
var get = document.getElementsByTagName;
for (var i = 0, i < 100000; i++) {
get('head');
}
}

13.使用變數緩衝值
在做重複工作的地方使用局部變數緩衝值。
下面的一組例子表明了儲存值到局部變數的廣泛意義。

例子1.計算執行前在迴圈體內使用變數儲存數學函數
錯誤的方法: 複製代碼 代碼如下:var d=35;
for (var i=0; i<1000; i++) {
y += Math.sin(d)*10;
}

更好的處理: 複製代碼 代碼如下:var d = 55;
var math_sind = Math.sin(d)*10;
for (var i=0; i<1000; i++) {
y += math_sind;
}

例子2.儲存數組的長度在迴圈中使用
糟糕的處理:
數組的長度每次都會被重複計算 複製代碼 代碼如下:for (var i = 0; i < arr.length; i++) {
// do something
}

更好的改進:
更好的方法是儲存數組的長度 複製代碼 代碼如下:for (var i = 0, len = arr.length; i < len; i++) {
// do something
}

總的來說,如果已經做了一次,我們就不需要重複的做不必要的工作。例如,範圍或者函數中多次使用到計算的一個運算式的值,儲存到變數可以使它多次被使用,否則我們會過頭的聲明一個變數並賦值然後只適用一次。所以請記住這些。

補充說明:
第2點
順便說一下JS主要不是編譯的是解釋的. 雖說不影響表達,但學術還是嚴謹點好.

第6點這是不是格式搞亂了?
a += ‘x' + ‘y';
運行更快的代碼:

a += 'x'; a += 'y';

9.避免在對象中使用不需要的DOM引用
new Object也是DOM引用?
這個應該是說不要輕意使用new Object()還有new Array(), 以及new Function() 一般情況使用 {...}, [...], f = function(..){...} 即:
這和上面那一點應該說的是一回事.

最後補充一個少用 位元運算, 因為js的所有數值運算最後(到JS引擎這層)都是全部轉得浮點來算的..所以位元運算可能反而更慢.

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

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.