Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
于想讓自己有一個提升,進不了一個更加廣闊的天地,總得找一個屬于自己的居所好好生存,所以平時會有意無意的去積累一些使用 jQuerry 的常用知識,特別是對于性能要求這一塊,總是會想是不是有更好的方式來實現(xiàn)。下面是我總結(jié)的一些小技巧,僅供參考。
一直在學習 javascript,也有看過《犀利開發(fā) Jquery 內(nèi)核詳解與實踐》,對這本書的評價只有兩個字犀利,可能是對 javascript 理解的還不夠透徹異或是自己太笨,更多的是自己不擅于思考懶得思考以至于里面說的一些精髓都沒有太深入的理解。
鑒于想讓自己有一個提升,進不了一個更加廣闊的天地,總得找一個屬于自己的居所好好生存,所以平時會有意無意的去積累一些使用 jQuerry 的常用知識,特別是對于性能要求這一塊,總是會想是不是有更好的方式來實現(xiàn)。
下面是我總結(jié)的一些小技巧,僅供參考。(我先會說一個總標題,然后用一小段話來說明這個意思 再最后用一個 demo 來簡單言明)
在一個函數(shù)中會用到全局對象存儲為局部變量來減少全局查找,因為訪問局部變量的速度要比訪問全局變量的速度更快些
function search() {
//當我要使用當前頁面地址和主機域名
alert(window.location.href + window.location.host);
}
//最好的方式是如下這樣 先用一個簡單變量保存起來
function search() {
var location=window.location;
alert(location.href + location.host);
}
定時器
如果針對的是不斷運行的代碼,不應(yīng)該使用 setTimeout,而應(yīng)該是用 setInterval,因為 setTimeout 每一次都會初始化一個定時器,而 setInterval 只會在開始的時候初始化一個定時器
var timeoutTimes=0;
function timeout() {
timeoutTimes++;
if (timeoutTimes < 10) {
setTimeout(timeout, 10);
}
}
timeout();
//可以替換為:
var intervalTimes=0;
function interval() {
intervalTimes++;
if (intervalTimes >=10) {
clearInterval(interv);
}
}
var interv=setInterval(interval, 10);
如果要連接多個字符串,應(yīng)該少使用 +=,如
s+=a;
s+=b;
s+=c;
1
2
3
應(yīng)該寫成 s+=a + b + c;
而如果是收集字符串,比如多次對同一個字符串進行 +=操作的話,最好使用一個緩存,使用 JavaScript 數(shù)組來收集,最后使用 join 方法連接起來
var buf=[];
for (var i=0; i < 100; i++) {
buf.push(i.toString());
}
var all=buf.join("");
和函數(shù)類似 ,with 語句會創(chuàng)建自己的作用域,因此會增加其中執(zhí)行的代碼的作用域鏈的長度,由于額外的作用域鏈的查找,在 with 語句中執(zhí)行的代碼肯定會比外面執(zhí)行的代碼要慢,在能不使用 with 語句的時候盡量不要使用 with 語句。
with (a.b.c.d) {
property1=1;
property2=2;
}
//可以替換為:
var obj=a.b.c.d;
obj.property1=1;
obj.property2=2;
數(shù)字轉(zhuǎn)換成字符串
般最好用 “” + 1 來將數(shù)字轉(zhuǎn)換成字符串,雖然看起來比較丑一點,但事實上這個效率是最高的,性能上來說:
("" +) > String() > .toString() > new String()
1
浮點數(shù)轉(zhuǎn)換成整型
很多人喜歡使用 parseInt(),其實 parseInt() 是用于將字符串轉(zhuǎn)換成數(shù)字,而不是浮點數(shù)和整型之間的轉(zhuǎn)換,我們應(yīng)該使用 Math.floor() 或者 Math.round()
各種類型轉(zhuǎn)換
var myVar="3.14159",
str="" + myVar, // to string
i_int=~ ~myVar, // to integer
f_float=1 * myVar, // to float
b_bool=!!myVar, /* to boolean - any string with length
and any number except 0 are true */
array=[myVar]; // to array
如果定義了 toString() 方法來進行類型轉(zhuǎn)換的話,推薦顯式調(diào)用 toString(),因為內(nèi)部的操作在嘗試所有可能性之后,會嘗試對象的 toString() 方法嘗試能否轉(zhuǎn)化為 String,所以直接調(diào)用這個方法效率會更高
在 JavaScript 中所有變量都可以使用單個 var 語句來聲明,這樣就是組合在一起的語句,以減少整個腳本的執(zhí)行時間,就如上面代碼一樣,上面代碼格式也挺規(guī)范,讓人一看就明了。
插入迭代器
如
var name=values[i]; i++;
1
前面兩條語句可以寫成
var name=values[i++]
1
使用直接量
var aTest=new Array(); //替換為
var aTest=[];
var aTest=new Object; //替換為
var aTest={};
var reg=new RegExp(); //替換為
var reg=/../;
//如果要創(chuàng)建具有一些特性的一般對象,也可以使用字面量,如下:
var oFruit=new O;
oFruit.color="red";
oFruit.name="apple";
//前面的代碼可用對象字面量來改寫成這樣:
var oFruit={ color: "red", name: "apple" };
一旦需要更新 DOM, 請考慮使用文檔碎片來構(gòu)建 DOM 結(jié)構(gòu),然后再將其添加到現(xiàn)存的文檔中。
for (var i=0; i < 1000; i++) {
var el=document.createElement('p');
el.innerHTML=i;
document.body.appendChild(el);
}
//可以替換為:
var frag=document.createDocumentFragment();
for (var i=0; i < 1000; i++) {
var el=document.createElement('p');
el.innerHTML=i;
frag.appendChild(el);
}
document.body.appendChild(frag);
使用一次 innerHTML 賦值代替構(gòu)建 dom 元素
對于大的 DOM 更改,使用 innerHTML 要比使用標準的 DOM 方法創(chuàng)建同樣的 DOM 結(jié)構(gòu)快得多。
var frag=document.createDocumentFragment();
for (var i=0; i < 1000; i++) {
var el=document.createElement('p');
el.innerHTML=i;
frag.appendChild(el);
}
document.body.appendChild(frag);
//可以替換為:
var html=[];
for (var i=0; i < 1000; i++) {
html.push('<p>' + i + '</p>');
}
document.body.innerHTML=html.join('');
通過模板元素 clone,替代 createElement
很多人喜歡在 JavaScript 中使用 document.write 來給頁面生成內(nèi)容。事實上這樣的效率較低,如果需要直接插入 HTML,可以找一個容器元素,比如指定一個 div 或者 span,并設(shè)置他們的 innerHTML 來將自己的 HTML 代碼插入到頁面中。通常我們可能會使用字符串直接寫 HTML 來創(chuàng)建節(jié)點,其實這樣做,1 無法保證代碼的有效性 2 字符串操作效率低,所以應(yīng)該是用 document.createElement() 方法,而如果文檔中存在現(xiàn)成的樣板節(jié)點,應(yīng)該是用 cloneNode() 方法,因為使用 createElement() 方法之后,你需要設(shè)置多次元素的屬性,使用 cloneNode() 則可以減少屬性的設(shè)置次數(shù)——同樣如果需要創(chuàng)建很多元素,應(yīng)該先準備一個樣板節(jié)點
var frag=document.createDocumentFragment();
for (var i=0; i < 1000; i++) {
var el=document.createElement('p');
el.innerHTML=i;
frag.appendChild(el);
}
document.body.appendChild(frag);
//替換為:
var frag=document.createDocumentFragment();
var pEl=document.getElementsByTagName('p')[0];
for (var i=0; i < 1000; i++) {
var el=pEl.cloneNode(false);
el.innerHTML=i;
frag.appendChild(el);
}
document.body.appendChild(frag);
使用 firstChild 和 nextSibling 代替 childNodes 遍歷 dom 元素
var nodes=element.childNodes;
for (var i=0, l=nodes.length; i < l; i++) {
var node=nodes[i];
//……
}
//可以替換為:
var node=element.firstChild;
while (node) {
//……
node=node.nextSibling;
刪除 DOM 節(jié)點
刪除 dom 節(jié)點之前, 一定要刪除注冊在該節(jié)點上的事件, 不管是用 observe 方式還是用 attachEvent 方式注冊的事件, 否則將會產(chǎn)生無法回收的內(nèi)存。另外,在 removeChild 和 innerHTML=’’二者之間, 盡量選擇后者. 因為在 sIEve(內(nèi)存泄露監(jiān)測工具) 中監(jiān)測的結(jié)果是用 removeChild 無法有效地釋放 dom 節(jié)點
使用事件代理
任何可以冒泡的事件都不僅僅可以在事件目標上進行處理,目標的任何祖先節(jié)點上也能處理,使用這個知識就可以將事件處理程序附加到更高的地方負責多個目標的事件處理,同樣,對于內(nèi)容動態(tài)增加并且子節(jié)點都需要相同的事件處理函數(shù)的情況,可以把事件注冊提到父節(jié)點上,這樣就不需要為每個子節(jié)點注冊事件監(jiān)聽了。另外,現(xiàn)有的 js 庫都采用 observe 方式來創(chuàng)建事件監(jiān)聽, 其實現(xiàn)上隔離了 dom 對象和事件處理函數(shù)之間的循環(huán)引用, 所以應(yīng)該盡量采用這種方式來創(chuàng)建事件監(jiān)聽
重復(fù)使用的調(diào)用結(jié)果,事先保存到局部變量
//避免多次取值的調(diào)用開銷
var h1=element1.clientHeight + num1;
var h4=element1.clientHeight + num2;
//可以替換為:
var eleHeight=element1.clientHeight;
var h1=eleHeight + num1;
var h4=eleHeight + num2;
注意 NodeList
最小化訪問 NodeList 的次數(shù)可以極大的改進腳本的性能
var images=document.getElementsByTagName('img');
for (var i=0, len=images.length; i < len; i++) {
}
編寫 JavaScript 的時候一定要知道何時返回 NodeList 對象,這樣可以最小化對它們的訪問
要了解了當使用 NodeList 對象時,合理使用會極大的提升代碼執(zhí)行速度
優(yōu)化循環(huán)
可以使用下面幾種方式來優(yōu)化循環(huán)
大多數(shù)循環(huán)使用一個從 0 開始、增加到某個特定值的迭代器,在很多情況下,從最大值開始,在循環(huán)中不斷減值的迭代器更加高效
由于每次循環(huán)過程都會計算終止條件,所以必須保證它盡可能快,也就是說避免屬性查找或者其它的操作,最好是將循環(huán)控制量保存到局部變量中,也就是說對數(shù)組或列表對象的遍歷時,提前將 length 保存到局部變量中,避免在循環(huán)的每一步重復(fù)取值。
var list=document.getElementsByTagName('p');
for (var i=0; i < list.length; i++) {
//……
}
//替換為:
var list=document.getElementsByTagName('p');
for (var i=0, l=list.length; i < l; i++) {
//……
}
循環(huán)體是執(zhí)行最多的,所以要確保其被最大限度的優(yōu)化
在 JavaScript 中,我們可以使用 for(;,while(),for(in) 三種循環(huán),事實上,這三種循環(huán)中 for(in) 的效率極差,因為他需要查詢散列鍵,只要可以,就應(yīng)該盡量少用。for(; 和 while 循環(huán),while 循環(huán)的效率要優(yōu)于 for(;,可能是因為 for(; 結(jié)構(gòu)的問題,需要經(jīng)常跳轉(zhuǎn)回去。
var arr=[1, 2, 3, 4, 5, 6, 7];
var sum=0;
for (var i=0, l=arr.length; i < l; i++) {
sum +=arr[i];
}
//可以考慮替換為:
var arr=[1, 2, 3, 4, 5, 6, 7];
var sum=0, l=arr.length;
while (l--) {
sum +=arr[l];
}
最常用的 for 循環(huán)和 while 循環(huán)都是前測試循環(huán),而如 do-while 這種后測試循環(huán),可以避免最初終止條件的計算,因此運行更快。
展開循環(huán)
當循環(huán)次數(shù)是確定的,消除循環(huán)并使用多次函數(shù)調(diào)用往往會更快。
避免雙重解釋
如果要提高代碼性能,盡可能避免出現(xiàn)需要按照 JavaScript 解釋的字符串,也就是
盡量少使用 eval 函數(shù)
使用 eval 相當于在運行時再次調(diào)用解釋引擎對內(nèi)容進行運行,需要消耗大量時間,而且使用 Eval 帶來的安全性問題也是不容忽視的。
不要使用 Function 構(gòu)造器
不要給 setTimeout 或者 setInterval 傳遞字符串參數(shù)
var num=0;
setTimeout('num++', 10);
//可以替換為:
var num=0;
function addNum() {
num++;
}
setTimeout(addNum, 10);
縮短否定檢測
if (oTest !='#ff0000') {
//do something
}
if (oTest !=null) {
//do something
}
if (oTest !=false) {
//do something
}
//雖然這些都正確,但用邏輯非操作符來操作也有同樣的效果:
if (!oTest) {
//do something
}
條件分支
將條件分支,按可能性順序從高到低排列:可以減少解釋器對條件的探測次數(shù)
在同一條件子的多(>2)條件分支時,使用 switch 優(yōu)于 if:switch 分支選擇的效率高于 if,在 IE 下尤為明顯。4 分支的測試,IE 下 switch 的執(zhí)行時間約為 if 的一半。
使用三目運算符替代條件分支
if (a > b) {
num=a;
} else {
num=b;
}
//可以替換為:
num=a > b ? a : b;
使用常量
避免與 null 進行比較
由于 JavaScript 是弱類型的,所以它不會做任何的自動類型檢查,所以如果看到與 null 進行比較的代碼,嘗試使用以下技術(shù)替換
避免全局量
全局變量應(yīng)該全部字母大寫,各單詞之間用_下劃線來連接。盡可能避免全局變量和函數(shù), 盡量減少全局變量的使用,因為在一個頁面中包含的所有 JavaScript 都在同一個域中運行。所以如果你的代碼中聲明了全局變量或者全局函數(shù)的話,后面的代碼中載入的腳本文件中的同名變量和函數(shù)會覆蓋掉(overwrite)你的。
//糟糕的全局變量和全局函數(shù)
var current=null;
function init(){
//...
}
function change() {
//...
}
function verify() {
//...
}
//解決辦法有很多,Christian Heilmann建議的方法是:
//如果變量和函數(shù)不需要在“外面”引用,那么就可以使用一個沒有名字的方法將他們?nèi)及饋怼?
(function(){
var current=null;
function init() {
//...
}
function change() {
//...
}
function verify() {
//...
}
})();
//如果變量和函數(shù)需要在“外面”引用,需要把你的變量和函數(shù)放在一個“命名空間”中
//我們這里用一個function做命名空間而不是一個var,因為在前者中聲明function更簡單,而且能保護隱私數(shù)據(jù)
myNameSpace=function() {
var current=null;
function init() {
//...
}
function change() {
//...
}
function verify() {
//...
}
//所有需要在命名空間外調(diào)用的函數(shù)和屬性都要寫在return里面
return {
init: init,
//甚至你可以為函數(shù)和屬性命名一個別名
set: change
};
};
尊重對象的所有權(quán)
因為 JavaScript 可以在任何時候修改任意對象,這樣就可以以不可預(yù)計的方式覆寫默認的行為,所以如果你不負責維護某個對象,它的對象或者它的方法,那么你就不要對它進行修改,具體一點就是說:
循環(huán)引用
如果循環(huán)引用中包含 DOM 對象或者 ActiveX 對象,那么就會發(fā)生內(nèi)存泄露。內(nèi)存泄露的后果是在瀏覽器關(guān)閉前,即使是刷新頁面,這部分內(nèi)存不會被瀏覽器釋放。
簡單的循環(huán)引用:
var el=document.getElementById('MyElement');
var func=function () {
//…
}
el.func=func;
func.element=el;
但是通常不會出現(xiàn)這種情況。通常循環(huán)引用發(fā)生在為 dom 元素添加閉包作為 expendo 的時候。
function init() {
var el=document.getElementById('MyElement');
el.onclick=function () {
//……
}
}
init();
init 在執(zhí)行的時候,當前上下文我們叫做 context。這個時候,context 引用了 el,el 引用了 function,function 引用了 context。這時候形成了一個循環(huán)引用。
下面 2 種方法可以解決循環(huán)引用:
1.置空 dom 對象
function init() {
var el=document.getElementById('MyElement');
el.onclick=function () {
//……
}
}
init();
//可以替換為:
function init() {
var el=document.getElementById('MyElement');
el.onclick=function () {
//……
}
el=null;
}
init();
將 el 置空,context 中不包含對 dom 對象的引用,從而打斷循環(huán)應(yīng)用。
如果我們需要將 dom 對象返回,可以用如下方法:
function init() {
var el=document.getElementById('MyElement');
el.onclick=function () {
//……
}
return el;
}
init();
//可以替換為:
function init() {
var el=document.getElementById('MyElement');
el.onclick=function () {
//……
}
try {
return el;
} finally {
el=null;
}
}
init();
2. 構(gòu)造新的 context
function init() {
var el=document.getElementById('MyElement');
el.onclick=function () {
//……
}
}
init();
//可以替換為:
function elClickHandler() {
//……
}
function init() {
var el=document.getElementById('MyElement');
el.onclick=elClickHandler;
}
init();
把 function 抽到新的 context 中,這樣,function 的 context 就不包含對 el 的引用,從而打斷循環(huán)引用。
通過 javascript 創(chuàng)建的 dom 對象,必須 append 到頁面中
IE 下,腳本創(chuàng)建的 dom 對象,如果沒有 append 到頁面中,刷新頁面,這部分內(nèi)存是不會回收的!
function create() {
var gc=document.getElementById('GC');
for (var i=0; i < 5000; i++) {
var el=document.createElement('div');
el.innerHTML="test";
//下面這句可以注釋掉,看看瀏覽器在任務(wù)管理器中,點擊按鈕然后刷新后的內(nèi)存變化
gc.appendChild(el);
}
}
釋放 dom 元素占用的內(nèi)存
將 dom 元素的 innerHTML 設(shè)置為空字符串,可以釋放其子元素占用的內(nèi)存。
在 rich 應(yīng)用中,用戶也許會在一個頁面上停留很長時間,可以使用該方法釋放積累得越來越多的 dom 元素使用的內(nèi)存。
釋放 javascript 對象
在 rich 應(yīng)用中,隨著實例化對象數(shù)量的增加,內(nèi)存消耗會越來越大。所以應(yīng)當及時釋放對對象的引用,讓 GC 能夠回收這些內(nèi)存控件。
對象: obj=null
對象屬性: delete obj.myproperty
數(shù)組 item:使用數(shù)組的 splice 方法釋放數(shù)組中不用的 item
避免 string 的隱式裝箱
對 string 的方法調(diào)用,比如’xxx’.length,瀏覽器會進行一個隱式的裝箱操作,將字符串先轉(zhuǎn)換成一個 String 對象。推薦對聲明有可能使用 String 實例方法的字符串時,采用如下寫法:
var myString=new String('Hello World');
松散耦合
1、解耦 HTML/JavaScript
JavaScript 和 HTML 的緊密耦合:直接寫在 HTML 中的 JavaScript、使用包含內(nèi)聯(lián)代碼的
HTML 和 JavaScript 的緊密耦合:JavaScript 中包含 HTML,然后使用 innerHTML 來插入一段 html 文本到頁面
其實應(yīng)該是保持層次的分離,這樣可以很容易的確定錯誤的來源,所以我們應(yīng)確保 HTML 呈現(xiàn)應(yīng)該盡可能與 JavaScript 保持分離
2、解耦 CSS/JavaScript
顯示問題的唯一來源應(yīng)該是 CSS,行為問題的唯一來源應(yīng)該是 JavaScript,層次之間保持松散耦合才可以讓你的應(yīng)用程序更加易于維護,所以像以下的代碼 element.style.color=“red” 盡量改為 element.className=“edit”,而且不要在 css 中通過表達式嵌入 JavaScript
3、解耦應(yīng)用程序 / 事件處理程序
將應(yīng)用邏輯和事件處理程序相分離:一個事件處理程序應(yīng)該從事件對象中提取,并將這些信息傳送給處理應(yīng)用邏輯的某個方法中。這樣做的好處首先可以讓你更容易更改觸發(fā)特定過程的事件,其次可以在不附加事件的情況下測試代碼,使其更易創(chuàng)建單元測試
性能方面的注意事項
1、盡量使用原生方法
2、switch 語句相對 if 較快
通過將 case 語句按照最可能到最不可能的順序進行組織
3、位運算較快
當進行數(shù)字運算時,位運算操作要比任何布爾運算或者算數(shù)運算快
4、巧用 ||和 && 布爾運算符
function eventHandler(e) {
if (!e) e=window.event;
}
//可以替換為:
function eventHandler(e) {
e=e || window.event;
}
if (myobj) {
doSomething(myobj);
}
//可以替換為:
myobj && doSomething(myobj);
避免錯誤應(yīng)注意的地方
1、每條語句末尾須加分號
在 if 語句中,即使條件表達式只有一條語句也要用 {} 把它括起來,以免后續(xù)如果添加了語句之后造成邏輯錯誤
2、使用 + 號時需謹慎
JavaScript 和其他編程語言不同的是,在 JavaScript 中,’+‘除了表示數(shù)字值相加,字符串相連接以外,還可以作一元運算符用,把字符串轉(zhuǎn)換為數(shù)字。因而如果使用不當,則可能與自增符’++'混淆而引起計算錯誤
var valueA=20;
var valueB="10";
alert(valueA + valueB); //ouput: 2010
alert(valueA + (+valueB)); //output: 30
alert(valueA + +valueB); //output:30
alert(valueA ++ valueB); //Compile error
3、使用 return 語句需要注意
一條有返回值的 return 語句不要用 () 括號來括住返回值,如果返回表達式,則表達式應(yīng)與 return 關(guān)鍵字在同一行,以避免壓縮時,壓縮工具自動加分號而造成返回與開發(fā)人員不一致的結(jié)果
function F1() {
var valueA=1;
var valueB=2;
return valueA + valueB;
}
function F2() {
var valueA=1;
var valueB=2;
return
valueA + valueB;
}
alert(F1()); //output: 3
alert(F2()); //ouput: undefined
避免在 if 和 while 語句的條件部分進行賦值,如 if (a=b),應(yīng)該寫成 if (a==b),但是在比較是否相等的情況下,最好使用全等運行符,也就是使用===和!==操作符會相對于==和!=會好點。==和!=操作符會進行類型強制轉(zhuǎn)換
var valueA="1";
var valueB=1;
if (valueA==valueB) {
alert("Equal");
}
else {
alert("Not equal");
}
//output: "Equal"
if (valueA===valueB) {
alert("Equal");
}
else {
alert("Not equal");
}
//output: "Not equal"
不要使用生偏語法
不要使用生偏語法,寫讓人迷惑的代碼,雖然計算機能夠正確識別并運行,但是晦澀難懂的代碼不方便以后維護
函數(shù)返回統(tǒng)一類型
雖然 JavaScript 是弱類型的,對于函數(shù)來說,前面返回整數(shù)型數(shù)據(jù),后面返回布爾值在編譯和運行都可以正常通過,但為了規(guī)范和以后維護時容易理解,應(yīng)保證函數(shù)應(yīng)返回統(tǒng)一的數(shù)據(jù)類型
總是檢查數(shù)據(jù)類型
要檢查你的方法輸入的所有數(shù)據(jù),一方面是為了安全性,另一方面也是為了可用性。用戶隨時隨地都會輸入錯誤的數(shù)據(jù)。這不是因為他們蠢,而是因為他們很忙,并且思考的方式跟你不同。用 typeof 方法來檢測你的 function 接受的輸入是否合法
何時用單引號,何時用雙引號
雖然在 JavaScript 當中,雙引號和單引號都可以表示字符串, 為了避免混亂,我們建議在 HTML 中使用雙引號,在 JavaScript 中使用單引號,但為了兼容各個瀏覽器,也為了解析時不會出錯,定義 JSON 對象時,最好使用雙引號
用 JSLint 運行 JavaScript 驗證器來確保沒有語法錯誤或者是代碼沒有潛在的問
部署之前推薦使用壓縮工具將 JS 文件壓縮
文件編碼統(tǒng)一用 UTF-8
JavaScript 程序應(yīng)該盡量放在 .js 的文件中,需要調(diào)用的時候在 HTML 中以
<html>
<head>
<title>DOM 教程</title>
</head>
<body>
<h1>DOM 第一課</h1>
<p class="example">Hello world!</p>
<input name="myInput" type="text" size="20" /><br />
<h1 id="myHeader">This is a header</h1>
</body>
</html>
上面的 HTML 中:
<html> 節(jié)點沒有父節(jié)點;它是根節(jié)點
<head> 和 <body> 的父節(jié)點是 <html> 節(jié)點
文本節(jié)點 "Hello world!" 的父節(jié)點是 <p> 節(jié)點
并且:
<html> 節(jié)點擁有兩個子節(jié)點:<head> 和 <body>
<head> 節(jié)點擁有一個子節(jié)點:<title> 節(jié)點
<title> 節(jié)點也擁有一個子節(jié)點:文本節(jié)點 "DOM 教程"
<h1> 和 <p> 節(jié)點是同胞節(jié)點, 同時也是 <body> 的子節(jié)點
并且:
<head> 元素是 <html> 元素的首個子節(jié)點
<body> 元素是 <html> 元素的最后一個子節(jié)點
<h1> 元素是 <body> 元素的首個子節(jié)點
<p> 元素是 <body> 元素的最后一個子節(jié)點
訪問節(jié)點:
var oLi=document.getElementsByTagName("li");
var oLi=document.getElementById("myHeader");
var oLi=document.getElementsByName("myInput"); //通過name屬性訪問
querySelector訪問方式: IE8開始支持, IE8以下不支持
var div=document.querySelector("#myHeader"); //通過id訪問
var div=document.querySelector("li"); //通過標簽訪問
document.querySelector(".example"); //通過class屬性訪問
獲取表單值
document.getElementById(id).value
querySelector() 方法返回文檔中匹配指定 CSS 選擇器的一個元素。
注意: querySelector() 方法僅僅返回匹配指定選擇器的第一個元素。
如果你需要返回所有的元素, 請使用 querySelectorAll() 方法替代。
利用父子兄關(guān)系查找節(jié)點:
使用childNodes屬性
對象屬性
nodeName 返回當前節(jié)點名字
元素節(jié)點的 nodeName 是標簽名稱
屬性節(jié)點的 nodeName 是屬性名稱
文本節(jié)點的 nodeName 永遠是 #text
文檔節(jié)點的 nodeName 永遠是 #document
nodeValue 返回當前節(jié)點的值, 僅對文本節(jié)點和屬性節(jié)點
對于文本節(jié)點, nodeValue 屬性包含文本。
對于屬性節(jié)點, nodeValue 屬性包含屬性值。
nodeValue 屬性對于文檔節(jié)點和元素節(jié)點是不可用的。
注意:nodeValue與tagName的區(qū)別對于空白節(jié)點的返回值:nodeValue返回null, tagName返回undefined
對于文本節(jié)點的返回值:nodeValue返回文本, tagName返回undefined
nodeType 檢測節(jié)點類型:
alert(document.nodeType);
元素節(jié)點的nodeType值為1; 標簽名稱
屬性節(jié)點的nodeType值為2; 屬性名稱 屬性節(jié)點不能算是其元素節(jié)點的子節(jié)點
文本節(jié)點的nodeType值為3; #text
注釋(Comment) 8: #comment
文檔(Document) 9 #document <HTML>
文檔類型(DocumentType) 10: <!DOCTYPE HTML PUBLIC"...">
節(jié)點 nodeType nodeName nodeValue
元素節(jié)點 1 大寫的標簽名 null
屬性節(jié)點 2 屬性名 屬性值
文本節(jié)點 3 #text 文本值
tagName 返回標簽的名稱, 僅對元素節(jié)點
parentNode 返回當前節(jié)點的父節(jié)點, 如果存在的話
childNodes 返回當前節(jié)點的子節(jié)點集合
firstChild 對標記的子節(jié)點集合中第一個節(jié)點的引用, 如果存在的話
lastChild 對標記的子節(jié)點集合中最后一個節(jié)點的引用, 如果存在的話
previousSibling 對同屬一個父節(jié)點的前一個兄弟節(jié)點的引用
nextSibling 對同屬一個父節(jié)點的下一個兄弟節(jié)點的引用
Attributes 返回當前節(jié)點(標記)屬性的列表 用于XML文件
ownerDocument 返回節(jié)點所屬的根元素
一些 DOM 對象方法
getElementById() 返回帶有指定 ID 的元素。
getElementsByTagName() 返回包含帶有指定標簽名稱的所有元素的節(jié)點列表(集合/節(jié)點數(shù)組)。
getElementsByName() 返回包含帶有指定類名的所有元素的節(jié)點列表。
appendChild() 把新的子節(jié)點添加到指定節(jié)點。
removeChild() 刪除子節(jié)點。
replaceChild() 替換子節(jié)點。
insertBefore() 在指定的子節(jié)點前面插入新的子節(jié)點。
createAttribute() 創(chuàng)建屬性節(jié)點。
createElement() 創(chuàng)建元素節(jié)點。
createTextNode() 創(chuàng)建文本節(jié)點。
getAttribute() 返回指定的屬性值。
setAttribute() 把指定屬性設(shè)置或修改為指定的值。
刪除、替換、插入子節(jié)點必須通過父節(jié)點的removeChild()方法來完成的
createAttribute() 創(chuàng)建屬性節(jié)點
var att=document.createAttribute("class");
att.value="democlass";
document.getElementsByTagName("H1")[0].setAttributeNode(att);
以上代碼可以簡化為
document.getElementsByTagName("H1")[0].class="democlass";
createAttribute()結(jié)合setAttributeNode()使用
等同于:
document.getElementsByTagName("H1")[0].setAttributeNode("class", "democlass");
DOM獲取所有子節(jié)點:
<html>
<head>
<title>childNodes</title>
<script language="javascript">
function myDOMInspector(){
var oUl=document.getElementById("myList"); //獲取<ul>標記
var DOMString="";
if(oUl.hasChildNodes()){ //判斷是否有子節(jié)點
var oCh=oUl.childNodes;
for(var i=0;i<oCh.length;i++) //逐一查找
DOMString +=oCh[i].nodeName + "\n";
}
alert(DOMString);
}
</script>
</head>
<body onload="myDOMInspector()">
<ul id="myList">
<li>糖醋排骨</li>
<li>圓籠粉蒸肉</li>
<li>泡菜魚</li>
<li>板栗燒雞</li>
<li>麻婆豆腐</li>
</ul>
</body>
</html>
使用parentNode屬性:
<html>
<head>
<title>parentNode</title>
<script language="javascript">
function myDOMInspector(){
var myItem=document.getElementById("myDearFood");
alert(myItem.parentNode.tagName); //返回值為ul
}
</script>
</head>
<body onload="myDOMInspector()">
<ul>
<li>糖醋排骨</li>
<li>圓籠粉蒸肉</li>
<li>泡菜魚</li>
<li id="myDearFood">板栗燒雞</li>
<li>麻婆豆腐</li>
</ul>
</body>
</html>
DOM的兄弟關(guān)系:
<html>
<head>
<title>Siblings</title>
<script language="javascript">
function myDOMInspector(){
var myItem=document.getElementById("myDearFood");
//訪問兄弟節(jié)點
var nextListItem=myItem.nextSibling;
var preListItem=myItem.previousSibling;
alert(nextListItem.tagName +" "+ preListItem.tagName);
}
</script>
</head>
<body onload="myDOMInspector()">
<ul>
<li>糖醋排骨</li>
<li>圓籠粉蒸肉</li>
<li>泡菜魚</li>
<li id="myDearFood">板栗燒雞</li>
<li>麻婆豆腐</li>
</ul>
</body>
</html>
編寫自定義函數(shù)解決Firefox等瀏覽器包含眾多的空格作為文本節(jié)點問題。
<html>
<head>
<title>Siblings</title>
<script language="javascript">
function nextSib(node){
var tempLast=node.parentNode.lastChild;
//判斷是否是最后一個節(jié)點,如果是則返回null
if(node==tempLast)
return null;
var tempObj=node.nextSibling;
//逐一搜索后面的兄弟節(jié)點,直到發(fā)現(xiàn)元素節(jié)點為止
while(tempObj.nodeType!=1 && tempObj.nextSibling!=null)
tempObj=tempObj.nextSibling;
//三目運算符,如果是元素節(jié)點則返回節(jié)點本身,否則返回null
return (tempObj.nodeType==1)?tempObj:null;
}
function prevSib(node){
var tempFirst=node.parentNode.firstChild;
//判斷是否是第一個節(jié)點,如果是則返回null
if(node==tempFirst)
return null;
var tempObj=node.previousSibling;
//逐一搜索前面的兄弟節(jié)點,直到發(fā)現(xiàn)元素節(jié)點為止
while(tempObj.nodeType!=1 && tempObj.previousSibling!=null)
tempObj=tempObj.previousSibling;
return (tempObj.nodeType==1)?tempObj:null;
}
function myDOMInspector(){
var myItem=document.getElementById("myDearFood");
//獲取后一個元素兄弟節(jié)點
var nextListItem=nextSib(myItem);
//獲取前一個元素兄弟節(jié)點
var preListItem=prevSib(myItem);
alert("后一項:" + ((nextListItem!=null)?nextListItem.firstChild.nodeValue:null) + " 前一項:" + ((preListItem!=null)?preListItem.firstChild.nodeValue:null) );
}
</script>
</head>
<body onload="myDOMInspector()">
<ul>
<li>糖醋排骨</li>
<li>圓籠粉蒸肉</li>
<li>泡菜魚</li>
<li id="myDearFood">板栗燒雞</li>
<li>麻婆豆腐</li>
</ul>
</body>
</html>
注意:最新版的IE瀏覽器也包含眾多的空格作為文本節(jié)點;
設(shè)置節(jié)點屬性:
getAttribute()方法和setAttibute()方法
<html>
<head>
<title>getAttribute()</title>
<script language="javascript">
function myDOMInspector(){
//獲取圖片
var myImg=document.getElementsByTagName("img")[0];
//獲取圖片title屬性
alert(myImg.getAttribute("title")); //也可以用myImg.title獲取屬性值
}
</script>
</head>
<body onload="myDOMInspector()">
<img src="01.jpg" title="情人坡" />
</body>
</html>
<html>
<head>
<title>setAttribute()</title>
<script language="javascript">
function changePic(){
//獲取圖片
var myImg=document.getElementsByTagName("img")[0];
//設(shè)置圖片src和title屬性
myImg.setAttribute("src","02.jpg"); //可以在屬性節(jié)點不存在時,添加節(jié)點的屬性值;
myImg.setAttribute("title","紫荊公寓"); //也可以通過myImg.title="紫荊公寓";
}
</script>
</head>
<body>
<img src="01.jpg" title="情人坡" onclick="changePic()" />
</body>
</html>
setAttribute()設(shè)置HTML標簽的屬性
oTable.setAttribute("border", "3"); //為表格邊框設(shè)置寬度
oTable.setAttribute("border", 3);
oTable.setAttribute("border", "3px"); //經(jīng)過測試, 此種寫法也正確
建議: 具體格式參照HTML屬性值的語法格式
setAttibute()設(shè)置行內(nèi)樣式
obj.setAttribute("style", "position:absolute;left:200px;top:200px");
注意:具體格式參考CSS樣式的語法格式
setAttibute()設(shè)置事件屬性
obj.setAttribute("onclick", "remove_img()"); //remove_img() 編寫自定義函數(shù), 這里不能使用自定義函數(shù)
注意:關(guān)于文本節(jié)點兼容性
元素節(jié)點
子節(jié)點: childNodes children
首尾子節(jié)點: firstChild firstElementChild
lastChild lastElementChild
兄弟節(jié)點: nextSibling nextElementSibling
previousSibling previousElementSibling
childNodes firstChild lastChild nextSibling previousSibling屬性IE6-IE8版本瀏覽器不會返回空白節(jié)點,
IE9以上版本瀏覽器會返回文本節(jié)點, W3C瀏覽器(包括火狐瀏覽器)也會返回文本節(jié)點
children firstElementChild lastElementChild nextElementSibling previousElementSibling 只返回元素節(jié)點, 不會返回空白節(jié)點
注意: DOM操作必須保住DOM節(jié)點必須存在, 當然也包括使用css樣式display:none隱藏的DOM節(jié)點, 否則會導(dǎo)致js語法錯誤;
于想讓自己有一個提升,進不了一個更加廣闊的天地,總得找一個屬于自己的居所好好生存,所以平時會有意無意的去積累一些使用 jQuerry 的常用知識,特別是對于性能要求這一塊,總是會想是不是有更好的方式來實現(xiàn)。下面是我總結(jié)的一些小技巧,僅供參考。
一直在學習 javascript,也有看過《犀利開發(fā) Jquery 內(nèi)核詳解與實踐》,對這本書的評價只有兩個字犀利,可能是對 javascript 理解的還不夠透徹異或是自己太笨,更多的是自己不擅于思考懶得思考以至于里面說的一些精髓都沒有太深入的理解。
鑒于想讓自己有一個提升,進不了一個更加廣闊的天地,總得找一個屬于自己的居所好好生存,所以平時會有意無意的去積累一些使用 jQuerry 的常用知識,特別是對于性能要求這一塊,總是會想是不是有更好的方式來實現(xiàn)。
下面是我總結(jié)的一些小技巧,僅供參考。(我先會說一個總標題,然后用一小段話來說明這個意思 再最后用一個 demo 來簡單言明)
在一個函數(shù)中會用到全局對象存儲為局部變量來減少全局查找,因為訪問局部變量的速度要比訪問全局變量的速度更快些
function search() {
//當我要使用當前頁面地址和主機域名
alert(window.location.href + window.location.host);
}
//最好的方式是如下這樣 先用一個簡單變量保存起來
function search() {
var location=window.location;
alert(location.href + location.host);
}
定時器
如果針對的是不斷運行的代碼,不應(yīng)該使用 setTimeout,而應(yīng)該是用 setInterval,因為 setTimeout 每一次都會初始化一個定時器,而 setInterval 只會在開始的時候初始化一個定時器
var timeoutTimes=0;
function timeout() {
timeoutTimes++;
if (timeoutTimes < 10) {
setTimeout(timeout, 10);
}
}
timeout();
//可以替換為:
var intervalTimes=0;
function interval() {
intervalTimes++;
if (intervalTimes >=10) {
clearInterval(interv);
}
}
var interv=setInterval(interval, 10);
如果要連接多個字符串,應(yīng)該少使用 +=,如
s+=a;
s+=b;
s+=c;
1
2
3
應(yīng)該寫成 s+=a + b + c;
而如果是收集字符串,比如多次對同一個字符串進行 +=操作的話,最好使用一個緩存,使用 JavaScript 數(shù)組來收集,最后使用 join 方法連接起來
var buf=[];
for (var i=0; i < 100; i++) {
buf.push(i.toString());
}
var all=buf.join("");
和函數(shù)類似 ,with 語句會創(chuàng)建自己的作用域,因此會增加其中執(zhí)行的代碼的作用域鏈的長度,由于額外的作用域鏈的查找,在 with 語句中執(zhí)行的代碼肯定會比外面執(zhí)行的代碼要慢,在能不使用 with 語句的時候盡量不要使用 with 語句。
with (a.b.c.d) {
property1=1;
property2=2;
}
//可以替換為:
var obj=a.b.c.d;
obj.property1=1;
obj.property2=2;
數(shù)字轉(zhuǎn)換成字符串
般最好用 “” + 1 來將數(shù)字轉(zhuǎn)換成字符串,雖然看起來比較丑一點,但事實上這個效率是最高的,性能上來說:
("" +) > String() > .toString() > new String()
1
浮點數(shù)轉(zhuǎn)換成整型
很多人喜歡使用 parseInt(),其實 parseInt() 是用于將字符串轉(zhuǎn)換成數(shù)字,而不是浮點數(shù)和整型之間的轉(zhuǎn)換,我們應(yīng)該使用 Math.floor() 或者 Math.round()
各種類型轉(zhuǎn)換
var myVar="3.14159",
str="" + myVar, // to string
i_int=~ ~myVar, // to integer
f_float=1 * myVar, // to float
b_bool=!!myVar, /* to boolean - any string with length
and any number except 0 are true */
array=[myVar]; // to array
如果定義了 toString() 方法來進行類型轉(zhuǎn)換的話,推薦顯式調(diào)用 toString(),因為內(nèi)部的操作在嘗試所有可能性之后,會嘗試對象的 toString() 方法嘗試能否轉(zhuǎn)化為 String,所以直接調(diào)用這個方法效率會更高
在 JavaScript 中所有變量都可以使用單個 var 語句來聲明,這樣就是組合在一起的語句,以減少整個腳本的執(zhí)行時間,就如上面代碼一樣,上面代碼格式也挺規(guī)范,讓人一看就明了。
插入迭代器
如
var name=values[i]; i++;
1
前面兩條語句可以寫成
var name=values[i++]
1
使用直接量
var aTest=new Array(); //替換為
var aTest=[];
var aTest=new Object; //替換為
var aTest={};
var reg=new RegExp(); //替換為
var reg=/../;
//如果要創(chuàng)建具有一些特性的一般對象,也可以使用字面量,如下:
var oFruit=new O;
oFruit.color="red";
oFruit.name="apple";
//前面的代碼可用對象字面量來改寫成這樣:
var oFruit={ color: "red", name: "apple" };
一旦需要更新 DOM, 請考慮使用文檔碎片來構(gòu)建 DOM 結(jié)構(gòu),然后再將其添加到現(xiàn)存的文檔中。
for (var i=0; i < 1000; i++) {
var el=document.createElement('p');
el.innerHTML=i;
document.body.appendChild(el);
}
//可以替換為:
var frag=document.createDocumentFragment();
for (var i=0; i < 1000; i++) {
var el=document.createElement('p');
el.innerHTML=i;
frag.appendChild(el);
}
document.body.appendChild(frag);
使用一次 innerHTML 賦值代替構(gòu)建 dom 元素
對于大的 DOM 更改,使用 innerHTML 要比使用標準的 DOM 方法創(chuàng)建同樣的 DOM 結(jié)構(gòu)快得多。
var frag=document.createDocumentFragment();
for (var i=0; i < 1000; i++) {
var el=document.createElement('p');
el.innerHTML=i;
frag.appendChild(el);
}
document.body.appendChild(frag);
//可以替換為:
var html=[];
for (var i=0; i < 1000; i++) {
html.push('<p>' + i + '</p>');
}
document.body.innerHTML=html.join('');
通過模板元素 clone,替代 createElement
很多人喜歡在 JavaScript 中使用 document.write 來給頁面生成內(nèi)容。事實上這樣的效率較低,如果需要直接插入 HTML,可以找一個容器元素,比如指定一個 div 或者 span,并設(shè)置他們的 innerHTML 來將自己的 HTML 代碼插入到頁面中。通常我們可能會使用字符串直接寫 HTML 來創(chuàng)建節(jié)點,其實這樣做,1 無法保證代碼的有效性 2 字符串操作效率低,所以應(yīng)該是用 document.createElement() 方法,而如果文檔中存在現(xiàn)成的樣板節(jié)點,應(yīng)該是用 cloneNode() 方法,因為使用 createElement() 方法之后,你需要設(shè)置多次元素的屬性,使用 cloneNode() 則可以減少屬性的設(shè)置次數(shù)——同樣如果需要創(chuàng)建很多元素,應(yīng)該先準備一個樣板節(jié)點
var frag=document.createDocumentFragment();
for (var i=0; i < 1000; i++) {
var el=document.createElement('p');
el.innerHTML=i;
frag.appendChild(el);
}
document.body.appendChild(frag);
//替換為:
var frag=document.createDocumentFragment();
var pEl=document.getElementsByTagName('p')[0];
for (var i=0; i < 1000; i++) {
var el=pEl.cloneNode(false);
el.innerHTML=i;
frag.appendChild(el);
}
document.body.appendChild(frag);
使用 firstChild 和 nextSibling 代替 childNodes 遍歷 dom 元素
var nodes=element.childNodes;
for (var i=0, l=nodes.length; i < l; i++) {
var node=nodes[i];
//……
}
//可以替換為:
var node=element.firstChild;
while (node) {
//……
node=node.nextSibling;
刪除 DOM 節(jié)點
刪除 dom 節(jié)點之前, 一定要刪除注冊在該節(jié)點上的事件, 不管是用 observe 方式還是用 attachEvent 方式注冊的事件, 否則將會產(chǎn)生無法回收的內(nèi)存。另外,在 removeChild 和 innerHTML=’’二者之間, 盡量選擇后者. 因為在 sIEve(內(nèi)存泄露監(jiān)測工具) 中監(jiān)測的結(jié)果是用 removeChild 無法有效地釋放 dom 節(jié)點
使用事件代理
任何可以冒泡的事件都不僅僅可以在事件目標上進行處理,目標的任何祖先節(jié)點上也能處理,使用這個知識就可以將事件處理程序附加到更高的地方負責多個目標的事件處理,同樣,對于內(nèi)容動態(tài)增加并且子節(jié)點都需要相同的事件處理函數(shù)的情況,可以把事件注冊提到父節(jié)點上,這樣就不需要為每個子節(jié)點注冊事件監(jiān)聽了。另外,現(xiàn)有的 js 庫都采用 observe 方式來創(chuàng)建事件監(jiān)聽, 其實現(xiàn)上隔離了 dom 對象和事件處理函數(shù)之間的循環(huán)引用, 所以應(yīng)該盡量采用這種方式來創(chuàng)建事件監(jiān)聽
重復(fù)使用的調(diào)用結(jié)果,事先保存到局部變量
//避免多次取值的調(diào)用開銷
var h1=element1.clientHeight + num1;
var h4=element1.clientHeight + num2;
//可以替換為:
var eleHeight=element1.clientHeight;
var h1=eleHeight + num1;
var h4=eleHeight + num2;
注意 NodeList
最小化訪問 NodeList 的次數(shù)可以極大的改進腳本的性能
var images=document.getElementsByTagName('img');
for (var i=0, len=images.length; i < len; i++) {
}
編寫 JavaScript 的時候一定要知道何時返回 NodeList 對象,這樣可以最小化對它們的訪問
要了解了當使用 NodeList 對象時,合理使用會極大的提升代碼執(zhí)行速度
優(yōu)化循環(huán)
可以使用下面幾種方式來優(yōu)化循環(huán)
大多數(shù)循環(huán)使用一個從 0 開始、增加到某個特定值的迭代器,在很多情況下,從最大值開始,在循環(huán)中不斷減值的迭代器更加高效
由于每次循環(huán)過程都會計算終止條件,所以必須保證它盡可能快,也就是說避免屬性查找或者其它的操作,最好是將循環(huán)控制量保存到局部變量中,也就是說對數(shù)組或列表對象的遍歷時,提前將 length 保存到局部變量中,避免在循環(huán)的每一步重復(fù)取值。
var list=document.getElementsByTagName('p');
for (var i=0; i < list.length; i++) {
//……
}
//替換為:
var list=document.getElementsByTagName('p');
for (var i=0, l=list.length; i < l; i++) {
//……
}
循環(huán)體是執(zhí)行最多的,所以要確保其被最大限度的優(yōu)化
在 JavaScript 中,我們可以使用 for(;,while(),for(in) 三種循環(huán),事實上,這三種循環(huán)中 for(in) 的效率極差,因為他需要查詢散列鍵,只要可以,就應(yīng)該盡量少用。for(; 和 while 循環(huán),while 循環(huán)的效率要優(yōu)于 for(;,可能是因為 for(; 結(jié)構(gòu)的問題,需要經(jīng)常跳轉(zhuǎn)回去。
var arr=[1, 2, 3, 4, 5, 6, 7];
var sum=0;
for (var i=0, l=arr.length; i < l; i++) {
sum +=arr[i];
}
//可以考慮替換為:
var arr=[1, 2, 3, 4, 5, 6, 7];
var sum=0, l=arr.length;
while (l--) {
sum +=arr[l];
}
最常用的 for 循環(huán)和 while 循環(huán)都是前測試循環(huán),而如 do-while 這種后測試循環(huán),可以避免最初終止條件的計算,因此運行更快。
展開循環(huán)
當循環(huán)次數(shù)是確定的,消除循環(huán)并使用多次函數(shù)調(diào)用往往會更快。
避免雙重解釋
如果要提高代碼性能,盡可能避免出現(xiàn)需要按照 JavaScript 解釋的字符串,也就是
盡量少使用 eval 函數(shù)
使用 eval 相當于在運行時再次調(diào)用解釋引擎對內(nèi)容進行運行,需要消耗大量時間,而且使用 Eval 帶來的安全性問題也是不容忽視的。
不要使用 Function 構(gòu)造器
不要給 setTimeout 或者 setInterval 傳遞字符串參數(shù)
var num=0;
setTimeout('num++', 10);
//可以替換為:
var num=0;
function addNum() {
num++;
}
setTimeout(addNum, 10);
縮短否定檢測
if (oTest !='#ff0000') {
//do something
}
if (oTest !=null) {
//do something
}
if (oTest !=false) {
//do something
}
//雖然這些都正確,但用邏輯非操作符來操作也有同樣的效果:
if (!oTest) {
//do something
}
條件分支
將條件分支,按可能性順序從高到低排列:可以減少解釋器對條件的探測次數(shù)
在同一條件子的多(>2)條件分支時,使用 switch 優(yōu)于 if:switch 分支選擇的效率高于 if,在 IE 下尤為明顯。4 分支的測試,IE 下 switch 的執(zhí)行時間約為 if 的一半。
使用三目運算符替代條件分支
if (a > b) {
num=a;
} else {
num=b;
}
//可以替換為:
num=a > b ? a : b;
使用常量
避免與 null 進行比較
由于 JavaScript 是弱類型的,所以它不會做任何的自動類型檢查,所以如果看到與 null 進行比較的代碼,嘗試使用以下技術(shù)替換
避免全局量
全局變量應(yīng)該全部字母大寫,各單詞之間用_下劃線來連接。盡可能避免全局變量和函數(shù), 盡量減少全局變量的使用,因為在一個頁面中包含的所有 JavaScript 都在同一個域中運行。所以如果你的代碼中聲明了全局變量或者全局函數(shù)的話,后面的代碼中載入的腳本文件中的同名變量和函數(shù)會覆蓋掉(overwrite)你的。
//糟糕的全局變量和全局函數(shù)
var current=null;
function init(){
//...
}
function change() {
//...
}
function verify() {
//...
}
//解決辦法有很多,Christian Heilmann建議的方法是:
//如果變量和函數(shù)不需要在“外面”引用,那么就可以使用一個沒有名字的方法將他們?nèi)及饋怼?
(function(){
var current=null;
function init() {
//...
}
function change() {
//...
}
function verify() {
//...
}
})();
//如果變量和函數(shù)需要在“外面”引用,需要把你的變量和函數(shù)放在一個“命名空間”中
//我們這里用一個function做命名空間而不是一個var,因為在前者中聲明function更簡單,而且能保護隱私數(shù)據(jù)
myNameSpace=function() {
var current=null;
function init() {
//...
}
function change() {
//...
}
function verify() {
//...
}
//所有需要在命名空間外調(diào)用的函數(shù)和屬性都要寫在return里面
return {
init: init,
//甚至你可以為函數(shù)和屬性命名一個別名
set: change
};
};
尊重對象的所有權(quán)
因為 JavaScript 可以在任何時候修改任意對象,這樣就可以以不可預(yù)計的方式覆寫默認的行為,所以如果你不負責維護某個對象,它的對象或者它的方法,那么你就不要對它進行修改,具體一點就是說:
循環(huán)引用
如果循環(huán)引用中包含 DOM 對象或者 ActiveX 對象,那么就會發(fā)生內(nèi)存泄露。內(nèi)存泄露的后果是在瀏覽器關(guān)閉前,即使是刷新頁面,這部分內(nèi)存不會被瀏覽器釋放。
簡單的循環(huán)引用:
var el=document.getElementById('MyElement');
var func=function () {
//…
}
el.func=func;
func.element=el;
但是通常不會出現(xiàn)這種情況。通常循環(huán)引用發(fā)生在為 dom 元素添加閉包作為 expendo 的時候。
function init() {
var el=document.getElementById('MyElement');
el.onclick=function () {
//……
}
}
init();
init 在執(zhí)行的時候,當前上下文我們叫做 context。這個時候,context 引用了 el,el 引用了 function,function 引用了 context。這時候形成了一個循環(huán)引用。
下面 2 種方法可以解決循環(huán)引用:
1.置空 dom 對象
function init() {
var el=document.getElementById('MyElement');
el.onclick=function () {
//……
}
}
init();
//可以替換為:
function init() {
var el=document.getElementById('MyElement');
el.onclick=function () {
//……
}
el=null;
}
init();
將 el 置空,context 中不包含對 dom 對象的引用,從而打斷循環(huán)應(yīng)用。
如果我們需要將 dom 對象返回,可以用如下方法:
function init() {
var el=document.getElementById('MyElement');
el.onclick=function () {
//……
}
return el;
}
init();
//可以替換為:
function init() {
var el=document.getElementById('MyElement');
el.onclick=function () {
//……
}
try {
return el;
} finally {
el=null;
}
}
init();
2. 構(gòu)造新的 context
function init() {
var el=document.getElementById('MyElement');
el.onclick=function () {
//……
}
}
init();
//可以替換為:
function elClickHandler() {
//……
}
function init() {
var el=document.getElementById('MyElement');
el.onclick=elClickHandler;
}
init();
把 function 抽到新的 context 中,這樣,function 的 context 就不包含對 el 的引用,從而打斷循環(huán)引用。
通過 javascript 創(chuàng)建的 dom 對象,必須 append 到頁面中
IE 下,腳本創(chuàng)建的 dom 對象,如果沒有 append 到頁面中,刷新頁面,這部分內(nèi)存是不會回收的!
function create() {
var gc=document.getElementById('GC');
for (var i=0; i < 5000; i++) {
var el=document.createElement('div');
el.innerHTML="test";
//下面這句可以注釋掉,看看瀏覽器在任務(wù)管理器中,點擊按鈕然后刷新后的內(nèi)存變化
gc.appendChild(el);
}
}
釋放 dom 元素占用的內(nèi)存
將 dom 元素的 innerHTML 設(shè)置為空字符串,可以釋放其子元素占用的內(nèi)存。
在 rich 應(yīng)用中,用戶也許會在一個頁面上停留很長時間,可以使用該方法釋放積累得越來越多的 dom 元素使用的內(nèi)存。
釋放 javascript 對象
在 rich 應(yīng)用中,隨著實例化對象數(shù)量的增加,內(nèi)存消耗會越來越大。所以應(yīng)當及時釋放對對象的引用,讓 GC 能夠回收這些內(nèi)存控件。
對象: obj=null
對象屬性: delete obj.myproperty
數(shù)組 item:使用數(shù)組的 splice 方法釋放數(shù)組中不用的 item
避免 string 的隱式裝箱
對 string 的方法調(diào)用,比如’xxx’.length,瀏覽器會進行一個隱式的裝箱操作,將字符串先轉(zhuǎn)換成一個 String 對象。推薦對聲明有可能使用 String 實例方法的字符串時,采用如下寫法:
var myString=new String('Hello World');
松散耦合
1、解耦 HTML/JavaScript
JavaScript 和 HTML 的緊密耦合:直接寫在 HTML 中的 JavaScript、使用包含內(nèi)聯(lián)代碼的
HTML 和 JavaScript 的緊密耦合:JavaScript 中包含 HTML,然后使用 innerHTML 來插入一段 html 文本到頁面
其實應(yīng)該是保持層次的分離,這樣可以很容易的確定錯誤的來源,所以我們應(yīng)確保 HTML 呈現(xiàn)應(yīng)該盡可能與 JavaScript 保持分離
2、解耦 CSS/JavaScript
顯示問題的唯一來源應(yīng)該是 CSS,行為問題的唯一來源應(yīng)該是 JavaScript,層次之間保持松散耦合才可以讓你的應(yīng)用程序更加易于維護,所以像以下的代碼 element.style.color=“red” 盡量改為 element.className=“edit”,而且不要在 css 中通過表達式嵌入 JavaScript
3、解耦應(yīng)用程序 / 事件處理程序
將應(yīng)用邏輯和事件處理程序相分離:一個事件處理程序應(yīng)該從事件對象中提取,并將這些信息傳送給處理應(yīng)用邏輯的某個方法中。這樣做的好處首先可以讓你更容易更改觸發(fā)特定過程的事件,其次可以在不附加事件的情況下測試代碼,使其更易創(chuàng)建單元測試
性能方面的注意事項
1、盡量使用原生方法
2、switch 語句相對 if 較快
通過將 case 語句按照最可能到最不可能的順序進行組織
3、位運算較快
當進行數(shù)字運算時,位運算操作要比任何布爾運算或者算數(shù)運算快
4、巧用 ||和 && 布爾運算符
function eventHandler(e) {
if (!e) e=window.event;
}
//可以替換為:
function eventHandler(e) {
e=e || window.event;
}
if (myobj) {
doSomething(myobj);
}
//可以替換為:
myobj && doSomething(myobj);
避免錯誤應(yīng)注意的地方
1、每條語句末尾須加分號
在 if 語句中,即使條件表達式只有一條語句也要用 {} 把它括起來,以免后續(xù)如果添加了語句之后造成邏輯錯誤
2、使用 + 號時需謹慎
JavaScript 和其他編程語言不同的是,在 JavaScript 中,’+‘除了表示數(shù)字值相加,字符串相連接以外,還可以作一元運算符用,把字符串轉(zhuǎn)換為數(shù)字。因而如果使用不當,則可能與自增符’++'混淆而引起計算錯誤
var valueA=20;
var valueB="10";
alert(valueA + valueB); //ouput: 2010
alert(valueA + (+valueB)); //output: 30
alert(valueA + +valueB); //output:30
alert(valueA ++ valueB); //Compile error
3、使用 return 語句需要注意
一條有返回值的 return 語句不要用 () 括號來括住返回值,如果返回表達式,則表達式應(yīng)與 return 關(guān)鍵字在同一行,以避免壓縮時,壓縮工具自動加分號而造成返回與開發(fā)人員不一致的結(jié)果
function F1() {
var valueA=1;
var valueB=2;
return valueA + valueB;
}
function F2() {
var valueA=1;
var valueB=2;
return
valueA + valueB;
}
alert(F1()); //output: 3
alert(F2()); //ouput: undefined
避免在 if 和 while 語句的條件部分進行賦值,如 if (a=b),應(yīng)該寫成 if (a==b),但是在比較是否相等的情況下,最好使用全等運行符,也就是使用===和!==操作符會相對于==和!=會好點。==和!=操作符會進行類型強制轉(zhuǎn)換
var valueA="1";
var valueB=1;
if (valueA==valueB) {
alert("Equal");
}
else {
alert("Not equal");
}
//output: "Equal"
if (valueA===valueB) {
alert("Equal");
}
else {
alert("Not equal");
}
//output: "Not equal"
不要使用生偏語法
不要使用生偏語法,寫讓人迷惑的代碼,雖然計算機能夠正確識別并運行,但是晦澀難懂的代碼不方便以后維護
函數(shù)返回統(tǒng)一類型
雖然 JavaScript 是弱類型的,對于函數(shù)來說,前面返回整數(shù)型數(shù)據(jù),后面返回布爾值在編譯和運行都可以正常通過,但為了規(guī)范和以后維護時容易理解,應(yīng)保證函數(shù)應(yīng)返回統(tǒng)一的數(shù)據(jù)類型
總是檢查數(shù)據(jù)類型
要檢查你的方法輸入的所有數(shù)據(jù),一方面是為了安全性,另一方面也是為了可用性。用戶隨時隨地都會輸入錯誤的數(shù)據(jù)。這不是因為他們蠢,而是因為他們很忙,并且思考的方式跟你不同。用 typeof 方法來檢測你的 function 接受的輸入是否合法
何時用單引號,何時用雙引號
雖然在 JavaScript 當中,雙引號和單引號都可以表示字符串, 為了避免混亂,我們建議在 HTML 中使用雙引號,在 JavaScript 中使用單引號,但為了兼容各個瀏覽器,也為了解析時不會出錯,定義 JSON 對象時,最好使用雙引號
用 JSLint 運行 JavaScript 驗證器來確保沒有語法錯誤或者是代碼沒有潛在的問
部署之前推薦使用壓縮工具將 JS 文件壓縮
文件編碼統(tǒng)一用 UTF-8
JavaScript 程序應(yīng)該盡量放在 .js 的文件中,需要調(diào)用的時候在 HTML 中以
*請認真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。