我google一下,已有人翻譯了此文.比我翻譯的要好!是譯言站翻譯的
見url:
http://www.yeeyan.com/articles/view/92135/47626/dz
原文見:
http://code.google.com/intl/zh-CN/speed/articles/optimizing-javascript.html
不合適的地方,請大家指出來!希望對你有用!
優(yōu)化JavaScript代碼(Optimizing JavaScript code)
客戶端腳本讓你的應(yīng)用程序更動態(tài),但是瀏覽器解釋腳本會帶來低效率,不同客戶端的性能也是不同的.下面我們討論一些技巧和最好的實踐來優(yōu)化你的JavaScript代碼
使用字符串(working with string)
String連接會給IE6和IE7垃圾回收造成影響.盡管這些問題已經(jīng)得到在IE8解決--字符串連接的效率在IE8和其它非IE瀏覽器(如chrome)上有稍微提高.如果你的一大部分用戶群體在使用IE6,7,你應(yīng)該備加注意String的構(gòu)造方式.
看一下如下例子:
var veryLongMessage =
'This is a long string that due to our strict line length limit of' +
maxCharsPerLine +
' characters per line must be wrapped. ' +
percentWhoDislike +
'% of engineers dislike this rule. The line length limit is for ' +
' style purposes, but we don't want it to have a performance impact.' +
' So the question is how should we do the wrapping?';

嘗試用join來代替連接:
1
var veryLongMessage =
2
['This is a long string that due to our strict line length limit of',
3
maxCharsPerLine,
4
' characters per line must be wrapped. ',
5
percentWhoDislike,
6
'% of engineers dislike this rule. The line length limit is for ',
7
' style purposes, but we don't want it to have a performance impact.',
8
' So the question is how should we do the wrapping?'
9
].join();
10
同樣的,在循環(huán)和/或條件語句中通過連接來構(gòu)造字符串也是非常低效的.下面是一個錯誤的例子:
var fibonacciStr = 'First 20 Fibonacci Numbers';

for (var i = 0; i < 20; i++)
{
fibonacciStr += i + ' = ' + fibonacci(i) + '';
}
正確的方式如下:
var strBuilder = ['First 20 fibonacci numbers:'];

for (var i = 0; i < 20; i++)
{
strBuilder.push(i, ' = ', fibonacci(i));
}
var fibonacciStr = strBuilder.join('');

通過助手函數(shù)生成字符串(Building strings with portions coming from helper functions)
通過把字符串生成器傳遞到函數(shù)中來構(gòu)造一個長字符串,要避免臨時的String結(jié)果.
例如,假設(shè)函數(shù)buildMenuItemHtml_ 需要用文字串(literal)和變量來構(gòu)造String,并在內(nèi)部使用了String構(gòu)造器.而不是如下方式使用:
var strBuilder = [];

for (var i = 0, length = menuItems.length; i < length; i++)
{
strBuilder.push(this.buildMenuItemHtml_(menuItems[i]));
}
var menuHtml = strBuilder.join();
推薦如下方式:
var strBuilder = [];

for (var i = 0, length = menuItems.length; i < length; i++)
{
this.buildMenuItem_(menuItems[i], strBuilder);
}
var menuHtml = strBuilder.join();
定義類方法(Defining class methods)
下面的代碼是低效率的,每生成一次baz.Bar對象,就會為其生成一個方法和閉包.

baz.Bar = function()
{
// 構(gòu)造函數(shù), constructor body

this.foo = function()
{
// 方法,method body
};
}
推薦方式是:

baz.Bar = function()
{
// 構(gòu)造函數(shù),constructor body
};


baz.Bar.prototype.foo = function()
{
//方法, method body
};

這種方式,不管有多少個baz.Bar對象生成,僅僅只建立一個方法foo且沒有產(chǎn)生閉包.
初始化實例變量(Initializing instance variables)
在原型(prototype)上用值類型[value type](而不是引用類型[reference type])聲明或初始化實例變量.這避免了在每次調(diào)用構(gòu)造函數(shù)時運行不必要的初始化代碼.(有些情況下是不能這樣做的:實例變量的初始值依賴于構(gòu)造參數(shù)或是在構(gòu)造時的其它狀態(tài)的變量(some other state at time of construction))
例子:

foo.Bar = function()
{
this.prop1_ = 4;
this.prop2_ = true;
this.prop3_ = [];
this.prop4_ = 'blah';
};
推薦的寫法:

foo.Bar = function()
{
this.prop3_ = [];
};
foo.Bar.prototype.prop1_ = 4;
foo.Bar.prototype.prop2_ = true;
foo.Bar.prototype.prop4_ = 'blah';

避開閉包的陷阱(Avoiding pitfalls with closures)
閉包是JavaScript的一個強大且有用的特色;但是它有幾個缺點:
1,它們是常見的內(nèi)存泄漏源
2,生成閉包明顯的比生成不是閉包的內(nèi)部函數(shù)慢,比調(diào)用靜態(tài)函數(shù)更慢.例如

function setupAlertTimeout()
{
var msg = 'Message to alert';

window.setTimeout(function()
{ alert(msg); }, 100);
}
上面比下面的寫法要慢:(上面的代碼產(chǎn)生了閉包)

function setupAlertTimeout()
{

window.setTimeout(function()
{
var msg = 'Message to alert';
alert(msg);
}, 100);
}
上面比下面的寫法要慢:

function alertMsg()
{
var msg = 'Message to alert';
alert(msg);
}


function setupAlertTimeout()
{
window.setTimeout(alertMsg, 100);
}

3,它們增加了變量的作用域(scope chain)的層次.當瀏覽器解析屬性的時候,將會檢查所有層次的作用域.見下面的例子:
var a = 'a';

function createFunctionWithClosure()
{
var b = 'b';

return function ()
{
var c = 'c';
a;
b;
c;
};
}

var f = createFunctionWithClosure();
f();


當執(zhí)行f方法的時候,引用a比引用b慢,引用b比引用c慢.
IE中閉包更多信息可以查看 IE+JScript Performance Recommendations Part 3: JavaScript Code inefficiencies
避免用with(Avoiding with)
在你代碼中避免用with.它對性能有負面影響,因為它修改了作用域,在其它作用域查找變量的開銷很大.
避免瀏覽器的內(nèi)存泄露(Avoiding browser memory leaks)
內(nèi)存泄露是web應(yīng)用程序普遍存在的問題,它會產(chǎn)生巨大的性能危害(hit).隨著瀏覽器內(nèi)存使用增加,你的web應(yīng)用程序,用戶系統(tǒng)其它部分操作,將變的慢起來.大部分web應(yīng)用程序的內(nèi)存泄露是因為在 JavaScript腳本和Dom之間生成了循環(huán)引用(例如:javascript腳本和IE com結(jié)構(gòu)之間,javascript腳本和Firefox xpcom結(jié)構(gòu)之間)
下面是避免內(nèi)存泄漏一些經(jīng)驗法則:
使用事件系統(tǒng)關(guān)聯(lián)事件處理函數(shù)(Use an event system for attaching event handlers)
大部分的循環(huán)引用模式[DOM 元素-->事件處理函數(shù)(event handler)-->閉包(closure scope)-->DOM元素].為了避免這個問題,可以用經(jīng)過充分測試事件系統(tǒng)(event system)來關(guān)聯(lián)事件處理函數(shù)(event handlers),例如:Google doctype,Dojo,JQuery
另外,在IE中使用用內(nèi)聯(lián)事件函數(shù)(inline event handlers)會導(dǎo)致另一種的類型的泄露.這不是通常的循環(huán)引用類型的泄露,而是由內(nèi)部臨時匿名腳本對象產(chǎn)生的泄露.詳細信息,請看文章"DOM insertin Order Leak Model"和例子JavaScript Kit tutorial.
避免擴展(expando)屬性
擴展(expando)屬性是把任意JavaScript的屬性附加的到DOM元素上,這是循環(huán)引用產(chǎn)生的根源.你可以不產(chǎn)生內(nèi)存泄露來擴展(expando)屬性,但是這是很容易產(chǎn)生內(nèi)存泄露的.泄露模式是[DOM元素-->擴展(via expando)-->中間對象(intermediary object)-->DOM元素].最好是避免使用用它們.如果你用到它們,僅可使用原始類型(primitive types)作為值.如果不是原始類型,當擴展(expando)屬性不在使用的時候,要把它置為空.可以參考文章"Circular References"