一直在學習 javascript,也有看過《犀利開發 Jquery 內核詳解與實踐》,對這本書的評價只有兩個字犀利,多是對 javascript 理解的還不夠透徹異或是本身太笨,更多的是本身不擅於思考懶得思考以致於裏面說的一些精髓都沒有太深刻的理解。javascript
鑑於想讓本身有一個提高,進不了一個更加廣闊的天地,總得找一個屬於本身的居所好好生存,因此平時會有意無心的去積累一些使用 jQuerry 的經常使用知識,特別是對於性能要求這一塊,老是會想是否是有更好的方式來實現。css
下面是我總結的一些小技巧,僅供參考。(我先會說一個總標題,而後用一小段話來講明這個意思 再最後用一個 demo 來簡單言明)html
在一個函數中會用到全局對象存儲爲局部變量來減小全局查找,由於訪問局部變量的速度要比訪問全局變量的速度更快些前端
function search() {
java
//當我要使用當前頁面地址和主機域名
node
alert(window.location.href + window.location.host);
web
}
編程
//最好的方式是以下這樣 先用一個簡單變量保存起來
數組
function search() {
瀏覽器
var location = window.location;
alert(location.href + location.host);
}
若是針對的是不斷運行的代碼,不該該使用 setTimeout,而應該是用 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);
若是要鏈接多個字符串,應該少使用 +=,如
s+=a;
s+=b;
s+=c;
應該寫成 s+=a + b + c;
而若是是收集字符串,好比屢次對同一個字符串進行 += 操做的話,最好使用一個緩存,使用 JavaScript 數組來收集,最後使用 join 方法鏈接起來
var buf = [];
for (var i = 0; i < 100; i++) {
buf.push(i.toString());
}
var all = buf.join("");
和函數相似 ,with 語句會建立本身的做用域,所以會增長其中執行的代碼的做用域鏈的長度,因爲額外的做用域鏈的查找,在 with 語句中執行的代碼確定會比外面執行的代碼要慢,在能不使用 with 語句的時候儘可能不要使用 with 語句。
with (a.b.c.d) {
property1 = 1;
property2 = 2;
}
//能夠替換爲:
var obj = a.b.c.d;
obj.property1 = 1;
obj.property2 = 2;
般最好用 "" + 1 來將數字轉換成字符串,雖然看起來比較醜一點,但事實上這個效率是最高的,性能上來講:
("" +) > String() > .toString() > new String()
不少人喜歡使用 parseInt(),其實 parseInt() 是用於將字符串轉換成數字,而不是浮點數和整型之間的轉換,咱們應該使用 Math.floor() 或者 Math.round()
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() 方法來進行類型轉換的話,推薦顯式調用 toString(),由於內部的操做在嘗試全部可能性以後,會嘗試對象的 toString() 方法嘗試可否轉化爲 String,因此直接調用這個方法效率會更高
在 JavaScript 中全部變量均可以使用單個 var 語句來聲明,這樣就是組合在一塊兒的語句,以減小整個腳本的執行時間,就如上面代碼同樣,上面代碼格式也挺規範,讓人一看就明瞭。
如 var name=values[i]; i++
; 前面兩條語句能夠寫成 var name=values[i++]
var aTest = new Array(); //替換爲
var aTest = [];
var aTest = new Object; //替換爲
var aTest = {};
var reg = new RegExp(); //替換爲
var reg = /../;
//若是要建立具備一些特性的通常對象,也可使用字面量,以下:
var oFruit = new O;
oFruit.color = "red";
oFruit.name = "apple";
//前面的代碼可用對象字面量來改寫成這樣:
var oFruit = { color: "red", name: "apple" };
一旦須要更新 DOM, 請考慮使用文檔碎片來構建 DOM 結構,而後再將其添加到現存的文檔中。
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);
對於大的 DOM 更改,使用 innerHTML 要比使用標準的 DOM 方法建立一樣的 DOM 結構快得多。
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('');
不少人喜歡在 JavaScript 中使用 document.write 來給頁面生成內容。事實上這樣的效率較低,若是須要直接插入 HTML,能夠找一個容器元素,好比指定一個 div 或者 span,並設置他們的 innerHTML 來將本身的 HTML 代碼插入到頁面中。一般咱們可能會使用字符串直接寫 HTML 來建立節點,其實這樣作,1 沒法保證代碼的有效性 2 字符串操做效率低,因此應該是用 document.createElement() 方法,而若是文檔中存在現成的樣板節點,應該是用 cloneNode() 方法,由於使用 createElement() 方法以後,你須要設置屢次元素的屬性,使用 cloneNode() 則能夠減小屬性的設置次數——一樣若是須要建立不少元素,應該先準備一個樣板節點
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);
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 節點以前, 必定要刪除註冊在該節點上的事件, 無論是用 observe 方式仍是用 attachEvent 方式註冊的事件, 不然將會產生沒法回收的內存。另外,在 removeChild 和 innerHTML=’’兩者之間, 儘可能選擇後者. 由於在 sIEve(內存泄露監測工具) 中監測的結果是用 removeChild 沒法有效地釋放 dom 節點
任何能夠冒泡的事件都不只僅能夠在事件目標上進行處理,目標的任何祖先節點上也能處理,使用這個知識就能夠將事件處理程序附加到更高的地方負責多個目標的事件處理,一樣,對於內容動態增長而且子節點都須要相同的事件處理函數的狀況,能夠把事件註冊提到父節點上,這樣就不須要爲每一個子節點註冊事件監聽了。另外,現有的 js 庫都採用 observe 方式來建立事件監聽, 其實現上隔離了 dom 對象和事件處理函數之間的循環引用, 因此應該儘可能採用這種方式來建立事件監聽
//避免屢次取值的調用開銷
var h1 = element1.clientHeight + num1;
var h4 = element1.clientHeight + num2;
//能夠替換爲:
var eleHeight = element1.clientHeight;
var h1 = eleHeight + num1;
var h4 = eleHeight + num2;
最小化訪問 NodeList 的次數能夠極大的改進腳本的性能
var images = document.getElementsByTagName('img');
for (var i = 0, len = images.length; i < len; i++) {
}
編寫 JavaScript 的時候必定要知道什麼時候返回 NodeList 對象,這樣能夠最小化對它們的訪問
進行了對 getElementsByTagName() 的調用
獲取了元素的 childNodes 屬性
獲取了元素的 attributes 屬性
訪問了特殊的集合,如 document.forms、document.images 等等
要了解了當使用 NodeList 對象時,合理使用會極大的提高代碼執行速度
可使用下面幾種方式來優化循環
減值迭代
大多數循環使用一個從 0 開始、增長到某個特定值的迭代器,在不少狀況下,從最大值開始,在循環中不斷減值的迭代器更加高效
簡化終止條件
因爲每次循環過程都會計算終止條件,因此必須保證它儘量快,也就是說避免屬性查找或者其它的操做,最好是將循環控制量保存到局部變量中,也就是說對數組或列表對象的遍歷時,提早將 length 保存到局部變量中,避免在循環的每一步重複取值。
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++) {
//……
}
簡化循環體
循環體是執行最多的,因此要確保其被最大限度的優化
使用後測試循環
在 JavaScript 中,咱們可使用 for(;;),while(),for(in) 三種循環,事實上,這三種循環中 for(in) 的效率極差,由於他須要查詢散列鍵,只要能夠,就應該儘可能少用。for(;;) 和 while 循環,while 循環的效率要優於 for(;;),多是由於 for(;;) 結構的問題,須要常常跳轉回去。
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 循環和 while 循環都是前測試循環,而如 do-while 這種後測試循環,能夠避免最初終止條件的計算,所以運行更快。
當循環次數是肯定的,消除循環並使用屢次函數調用每每會更快。
若是要提升代碼性能,儘量避免出現須要按照 JavaScript 解釋的字符串,也就是
儘可能少使用 eval 函數
使用 eval 至關於在運行時再次調用解釋引擎對內容進行運行,須要消耗大量時間,並且使用 Eval 帶來的安全性問題也是不容忽視的。
不要使用 Function 構造器
不要給 setTimeout 或者 setInterval 傳遞字符串參數
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
}
將條件分支,按可能性順序從高到低排列:能夠減小解釋器對條件的探測次數
在同一條件子的多(>2)條件分支時,使用 switch 優於 if:switch 分支選擇的效率高於 if,在 IE 下尤其明顯。4 分支的測試,IE 下 switch 的執行時間約爲 if 的一半。
使用三目運算符替代條件分支
if (a > b) {
num = a;
} else {
num = b;
}
//能夠替換爲:
num = a > b ? a : b;
重複值: 任何在多處用到的值都應該抽取爲一個常量
用戶界面字符串: 任何用於顯示給用戶的字符串,都應該抽取出來以方便國際化
URLs: 在 Web 應用中,資源位置很容易變動,因此推薦用一個公共地方存放全部的 URL
任意可能會更改的值: 每當你用到字面量值的時候,你都要問一下本身這個值在將來是否是會變化,若是答案是 「是」,那麼這個值就應該被提取出來做爲一個常量。
因爲 JavaScript 是弱類型的,因此它不會作任何的自動類型檢查,因此若是看到與 null 進行比較的代碼,嘗試使用如下技術替換
若是值應爲一個引用類型,使用 instanceof 操做符檢查其構造函數
若是值應爲一個基本類型,做用 typeof 檢查其類型
若是是但願對象包含某個特定的方法名,則使用 typeof 操做符確保指定名字的方法存在於對象上
web前端/H5/javascript學習羣:250777811
歡迎關注此公衆號→【web前端EDU】跟大佬一塊兒學前端!歡迎你們留言討論一塊兒轉發
全局變量應該所有字母大寫,各單詞之間用_下劃線來鏈接。儘量避免全局變量和函數, 儘可能減小全局變量的使用,由於在一個頁面中包含的全部 JavaScript 都在同一個域中運行。因此若是你的代碼中聲明瞭全局變量或者全局函數的話,後面的代碼中載入的腳本文件中的同名變量和函數會覆蓋掉(overwrite)你的。
//糟糕的全局變量和全局函數
var current = null;
function init(){
//...
}
function change() {
//...
}
function verify() {
//...
}
//解決辦法有不少,Christian Heilmann建議的方法是:
//若是變量和函數不須要在「外面」引用,那麼就可使用一個沒有名字的方法將他們全都包起來。
(function(){
var current = null;
function init() {
//...
}
function change() {
//...
}
function verify() {
//...
}
})();
//若是變量和函數須要在「外面」引用,須要把你的變量和函數放在一個「命名空間」中
//咱們這裏用一個function作命名空間而不是一個var,由於在前者中聲明function更簡單,並且能保護隱私數據
myNameSpace = function() {
var current = null;
function init() {
//...
}
function change() {
//...
}
function verify() {
//...
}
//全部須要在命名空間外調用的函數和屬性都要寫在return裏面
return {
init: init,
//甚至你能夠爲函數和屬性命名一個別名
set: change
};
};
由於 JavaScript 能夠在任什麼時候候修改任意對象,這樣就能夠以不可預計的方式覆寫默認的行爲,因此若是你不負責維護某個對象,它的對象或者它的方法,那麼你就不要對它進行修改,具體一點就是說:
不要爲實例或原型添加屬性
不要爲實例或者原型添加方法
不要重定義已經存在的方法
不要重複定義其它團隊成員已經實現的方法,永遠不要修改不是由你全部的對象,你能夠經過如下方式爲對象建立新的功能:
建立包含所需功能的新對象,並用它與相關對象進行交互
建立自定義類型,繼承須要進行修改的類型,而後能夠爲自定義類型添加額外功能
若是循環引用中包含 DOM 對象或者 ActiveX 對象,那麼就會發生內存泄露。內存泄露的後果是在瀏覽器關閉前,即便是刷新頁面,這部份內存不會被瀏覽器釋放。
簡單的循環引用:
var el = document.getElementById('MyElement');
var func = function () {
//…
}
el.func = func;
func.element = el;
可是一般不會出現這種狀況。一般循環引用發生在爲 dom 元素添加閉包做爲 expendo 的時候。
function init() {
var el = document.getElementById('MyElement');
el.onclick = function () {
//……
}
}
init();
init 在執行的時候,當前上下文咱們叫作 context。這個時候,context 引用了 el,el 引用了 function,function 引用了 context。這時候造成了一個循環引用。
下面 2 種方法能夠解決循環引用:
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 對象的引用,從而打斷循環應用。
若是咱們須要將 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. 構造新的 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 的引用,從而打斷循環引用。
IE 下,腳本建立的 dom 對象,若是沒有 append 到頁面中,刷新頁面,這部份內存是不會回收的!
function create() {
var gc = document.getElementById('GC');
for (var i = 0; i < 5000; i++) {
var el = document.createElement('div');
el.innerHTML = "test";
//下面這句能夠註釋掉,看看瀏覽器在任務管理器中,點擊按鈕而後刷新後的內存變化
gc.appendChild(el);
}
}
將 dom 元素的 innerHTML 設置爲空字符串,能夠釋放其子元素佔用的內存。
在 rich 應用中,用戶也許會在一個頁面上停留很長時間,可使用該方法釋放積累得愈來愈多的 dom 元素使用的內存。
在 rich 應用中,隨着實例化對象數量的增長,內存消耗會愈來愈大。因此應當及時釋放對對象的引用,讓 GC 可以回收這些內存控件。
對象: obj = null
對象屬性: delete obj.myproperty
數組 item:使用數組的 splice 方法釋放數組中不用的 item
對 string 的方法調用,好比'xxx'.length,瀏覽器會進行一個隱式的裝箱操做,將字符串先轉換成一個 String 對象。推薦對聲明有可能使用 String 實例方法的字符串時,採用以下寫法:
var myString = new String('Hello World')
;
一、解耦 HTML/JavaScript
JavaScript 和 HTML 的緊密耦合:直接寫在 HTML 中的 JavaScript、使用包含內聯代碼的 <script>
元素、使用 HTML 屬性來分配事件處理程序等
HTML 和 JavaScript 的緊密耦合:JavaScript 中包含 HTML,而後使用 innerHTML 來插入一段 html 文本到頁面
其實應該是保持層次的分離,這樣能夠很容易的肯定錯誤的來源,因此咱們應確保 HTML 呈現應該儘量與 JavaScript 保持分離
二、解耦 CSS/JavaScript
顯示問題的惟一來源應該是 CSS,行爲問題的惟一來源應該是 JavaScript,層次之間保持鬆散耦合纔可讓你的應用程序更加易於維護,因此像如下的代碼 element.style.color="red"
儘可能改成 element.className="edit"
,並且不要在 css 中經過表達式嵌入 JavaScript
三、解耦應用程序 / 事件處理程序
將應用邏輯和事件處理程序相分離:一個事件處理程序應該從事件對象中提取,並將這些信息傳送給處理應用邏輯的某個方法中。這樣作的好處首先可讓你更容易更改觸發特定過程的事件,其次能夠在不附加事件的狀況下測試代碼,使其更易建立單元測試
一、儘可能使用原生方法
二、switch 語句相對 if 較快
經過將 case 語句按照最可能到最不可能的順序進行組織
三、位運算較快
當進行數字運算時,位運算操做要比任何布爾運算或者算數運算快
四、巧用 ||
和 &&
布爾運算符
function eventHandler(e) {
if (!e) e = window.event;
}
//能夠替換爲:
function eventHandler(e) {
e = e || window.event;
}
if (myobj) {
doSomething(myobj);
}
//能夠替換爲:
myobj && doSomething(myobj);
一、每條語句末尾須加分號
在 if 語句中,即便條件表達式只有一條語句也要用 {} 把它括起來,以避免後續若是添加了語句以後形成邏輯錯誤
二、使用 + 號時需謹慎
JavaScript 和其餘編程語言不一樣的是,在 JavaScript 中,'+'除了表示數字值相加,字符串相鏈接之外,還能夠做一元運算符用,把字符串轉換爲數字。於是若是使用不當,則可能與自增符'++'混淆而引發計算錯誤
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
三、使用 return 語句須要注意
一條有返回值的 return 語句不要用 () 括號來括住返回值,若是返回表達式,則表達式應與 return 關鍵字在同一行,以免壓縮時,壓縮工具自動加分號而形成返回與開發人員不一致的結果
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),應該寫成 if (a == b),可是在比較是否相等的狀況下,最好使用全等運行符,也就是使用 === 和!== 操做符會相對於 == 和!= 會好點。== 和!= 操做符會進行類型強制轉換
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"
不要使用生偏語法,寫讓人迷惑的代碼,雖然計算機可以正確識別並運行,可是晦澀難懂的代碼不方便之後維護
雖然 JavaScript 是弱類型的,對於函數來講,前面返回整數型數據,後面返回布爾值在編譯和運行均可以正常經過,但爲了規範和之後維護時容易理解,應保證函數應返回統一的數據類型
要檢查你的方法輸入的全部數據,一方面是爲了安全性,另外一方面也是爲了可用性。用戶隨時隨地都會輸入錯誤的數據。這不是由於他們蠢,而是由於他們很忙,而且思考的方式跟你不一樣。用 typeof 方法來檢測你的 function 接受的輸入是否合法
雖然在 JavaScript 當中,雙引號和單引號均可以表示字符串, 爲了不混亂,咱們建議在 HTML 中使用雙引號,在 JavaScript 中使用單引號,但爲了兼容各個瀏覽器,也爲了解析時不會出錯,定義 JSON 對象時,最好使用雙引號
web前端/H5/javascript學習羣:250777811
歡迎關注此公衆號→【web前端EDU】跟大佬一塊兒學前端!歡迎你們留言討論一塊兒轉發
用 JSLint 運行 JavaScript 驗證器來確保沒有語法錯誤或者是代碼沒有潛在的問
部署以前推薦使用壓縮工具將 JS 文件壓縮
文件編碼統一用 UTF-8
JavaScript 程序應該儘可能放在 .js 的文件中,須要調用的時候在 HTML 中以 <script src="filename.js">
的形式包含進來。JavaScript 代碼若不是該 HTML 文件所專用的,則應儘可能避免在 HTML 文件中直接編寫 JavaScript 代碼。由於這樣會大大增長 HTML 文件的大小,無益於代碼的壓縮和緩存的使用。另外, <script src="filename.js">
標籤應儘可能放在文件的後面, 最好是放在 </body >
標籤前。這樣會下降因加載 JavaScript 代碼而影響頁面中其它組件的加載時間。
永遠不要忽略代碼優化工做,重構是一項從項目開始到結束須要持續的工做,只有不斷的優化代碼才能讓代碼的執行效率愈來愈好。