譯文:Efficient JavaScript

原文連接:https://dev.opera.com/articles/efficient-javascript/?page=2#primitiveo...javascript

高效的JavaScript

曾經,一個Web頁面不會包含太多的腳本,或者至少來講,它們不會影響頁面的性能。然而,如今的Web頁面愈來愈像本地運用了,腳本的性能成了一個很大的影響隨着愈來愈多的運用轉向使用Web技術時,提升頁面的性能成爲了愈來愈重要的問題。java

ECMAScript

避免使用evalFunction構造函數

每次當evalFunction constructor經過字符串源碼形式調用時,腳本引擎必須開啓轉換機制將字符串源碼轉換成可執行的代碼。一般來講這是比較耗性能的。瀏覽器

eval調用特別的很差,當執行的內容字符串傳遞給eval時不能被提早執行,因爲代碼執行會被eval中執行的內容影響,那就意味着編譯器不能更好的優化執行上下文,而且瀏覽器在運行時時放棄執行下面的上下文。這樣就增長了額外的性能影響。函數

對於Function constructor來講,它的名聲和eval同樣也不太好,雖然使用它並不會影響上下文的執行,可是它執行的效率卻很低下。示例代碼以下:性能

錯誤的使用eval:優化

function getProperty(oString) {
    var oReference;
    eval('oReference = test.prop.' + oString);
    return oReference;
}

正確的姿式:this

function getProperty(oString) {
    return test.prop[oString];
}

錯誤的使用Function constructorcode

function addMethod(oObject, oProperty, oFunctionCode) {
    oObject[oProperty] = new Function(oFunctionCode);
}
addMethod(
    myObject,
    'rotateBy90',
    'this.angle = (this.angle + 90) % 360'
);
addMethod(
    myObject,
    'rotateBy60',
    'this.angle = (this.angle + 60) % 360'
);

正確的姿式:orm

function addMethod(oObject, oProperty, oFunction) {
    oObject[oProperty] = oFunction;
}
addMethod(
    myObject,
    'rotateBy90',
    function() {
        this.angle = (this.angle + 90) % 360;
    }
);
addMethod(
    myObject,
    'rotateBy60',
    function() {
        this.angle = (this.angle + 60) % 360;
    }
);

避免使用with

儘管對於開發人員來講,使用with比較方便,可是對性能來講,倒是很是消耗的。緣由是對腳本引擎來講,它會拓展做用域鏈,而查找變量的時候不會判斷是否被當前引用。儘管這種狀況帶來性能的開銷比較少,可是每次編譯的時候咱們都不知道內容的做用域,那就意味着編譯器不能對其進行優化,因此它就和普通的做用域同樣。對象

一種更有效的方法代替方法是使用一個對象變量來代替with的使用。屬性的訪問能夠經過對象的引用來實現。這樣工做起來很是有效,若是屬性不是基本類型外,好比字符串和布爾值。

考慮下面的代碼:

with(test.information.settings.files) {
    primary = 'names';
    secondary = 'roles';
    tertiary = 'references';
}

使用下面的方式效率更高:

var testObject = test.information.settings.files;
testObject.primary = 'names';
testObject.secondary = 'roles';
testObject.tertiary = 'references';

不要在循環的函數裏面使用try-catch-finally

try-catch-finally語句相對於其餘的語句來講它的結構很是惟一的,當腳本運行的時候它會在當前的做用域總建立一個變量,這發生在catch語句調用的時候.捕獲的異常對象會關聯這個變量,這個變量不會存在其餘的腳本里,即便是相同的做用域。它在catch語句開始的時候建立,在執行結束的時候銷燬它。

由於這個變量會在運行的時候建立和銷燬,因此會產生一種特殊的狀況,一些瀏覽器不能及時的在性能比較耗的循環中及時捕獲該句柄。因此會致使一些性能上的問題當異常被捕獲的時候。

若是可能的話,異常的執行應該在更高的級別執行,這樣它就不會頻繁的出現,或者經過檢查指望的動做最先被容許的話來避免,下面經過示例來講明:

錯誤的使用方式:

var oProperties = [
    'first',
    'second',
    'third',
    …
    'nth'
];
for(var i = 0; i < oProperties.length; i++) {
    try {
        test[oProperties[i]].someproperty = somevalue;
    } catch(e) {
        …
    }
}

在許多狀況下,try-catch-finally結構能夠移動到循環的外圍, 這樣看起來彷佛語意上有點改變。因爲異常拋出時,循環會被中斷,可是下面的代碼會依然執行。

var oProperties = [
    'first',
    'second',
    'third',
    …
    'nth'
];
try {
    for(var i = 0; i < oProperties.length; i++) {
        test[oProperties[i]].someproperty = somevalue;
    }
} catch(e) {
    …
}

在某些狀況下,try-catch-finally結構能夠避免使用。好比:

var oProperties = [
    'first',
    'second',
    'third',
    …
    'nth'
];
for(var i = 0; i < oProperties.length; i++) {
    if(test[oProperties[i]]) {
        test[oProperties[i]].someproperty = somevalue;
    }
}

隔離evalwith的使用

因爲這些結構影響性能如此的深,因此它們使用的越少越好。可是有時候你可能須要它們,若是一個函數被調用或者一個循環重複的被計算,最好的方式仍是避免使用這些結構,他們最好的解決方案就是被執行一次,或者極少數,以致於基本上對性能沒什麼影響。

避免使用全局變量

在全局範圍內建立一個變量是很誘惑的,只因它建立的方式很簡單,可是有一下幾個緣由會致使腳本運行變慢。

首先,全局變量須要腳本引擎查找到最外的做用域,查找速度比較慢,第二,全局變量經過window對象被分享,意味着本質上它有兩層做用域(??)。

注意對象的轉換

字面量,好比字符串,數字或者布爾值,在ECMAScript中有兩種表現,它門可能被看成單純的值或者一個對象。

任何屬性或者方法被調用的時候針對的是這個對象,不是這個值,當你引用一個屬性或者方法的時候,ECMAScript引擎會暗中的建立一個你值對應的字符串對象。在方法調用以前。這個對象只會被請求一次,當你嘗試下一次調用該值的某個方法時它又會被建立一次。來看看下面的例子:

var s = '0123456789';
for(var i = 0; i < s.length; i++) {
    s.charAt(i);
}

上面的例子須要腳本引擎建立21次字符串對象,一次length屬性的訪問,一次charAt方法的調用。

優化的方案以下所示:

var s = new String('0123456789');
for(var i = 0; i < s.length; i++) {
    s.charAt(i);
}

和上面等效,可是僅僅手動建立了一個對象,性能上要比上面的好不少。

注意:不一樣的瀏覽器,對於裝箱和拆箱的優化不同。

避免咋性能堪憂的函數裏使用for-in迭代

for-in迭代有它本身的特色,可是它常常被濫用,這種迭代須要腳本引擎建立一個全部可枚舉屬性的清單,並檢出爲看成副本,在開始枚舉的時候。

使用字符串的累加形式。

字符串的拼接是個昂貴的過程,當使用"+"運算符時,它不會把結果當即添加到變量中,反而它會建立一個新的字符串對象在內存中,並把獲得的結果賦值個這個字符串。而後這個新的字符串對象在賦值給變量。可是使用"+="能夠避免這樣的過程。

原始的操做符可能比函數調用更快

示例:

var min = Math.min(a,b);
A.push(v);

下面的方式和上面的等效,可是效率更高:

var min = a < b ? a : b;
A[A.length] = v;

傳遞一個回調函數而不是字符串給setTimeout()setInterval()

setTimeout()setInterval()方法傳遞的是個字符串時,它內部會調用eval,因此會致使性能上的問題。

相關文章
相關標籤/搜索