標籤:
1、帶有 src 屬性的<script>元素不應該在其<script>和</script>標籤之間再包含額外的 JavaScript 代碼。如果包含了嵌入的代碼,則只會下載並執行外部指令檔,嵌入的代碼會被忽略。一般都把全部 JavaScript 引用放在<body>元素中頁面內容的後面。
2、循環參考:對象 A 中包含一個指向對象 B 的指標,而對象 B 中也包含一個指向對象 A 的引用:
var element = document.getElementById("some_element");var myObject = new Object();myObject.element = element;element.someObject = myObject;
IE8及以前版本中有一部分對象並不是原生 JavaScript 對象,而是使用 C++以 COM(Component Object Model,元件物件模型)對象的形式實現的的,而 COM 物件的垃圾
收集機制由於採用了引用計數策略,所以會有循環參考的問題,而循環參考會導致即使將例子中的 DOM 從頁面中移除,它也永遠不會被回收,因此會導致記憶體泄露。所以一旦資料不再有用,最好通過將其值設定為 null 來釋放其引用:
myObject.element = null;element.someObject = null;
3、未初始化的變數會自動被賦予 undefined 值,但顯式地初始設定變數依然是明智的選擇,當 typeof 操作符返回"undefined"值時,我們就知道被檢測的變數還沒有被聲明,而不是尚未初始化。
4、建立對象推薦組合使用建構函式模式和原型模式:
function Person(name, age, job){ this.name = name; this.age = age; this.job = job; this.friends = ["Shelby", "Court"];}Person.prototype = { constructor : Person, sayName : function(){ alert(this.name); }}var person1 = new Person("Nicholas", 29, "Software Engineer");var person2 = new Person("Greg", 27, "Doctor");person1.friends.push("Van");alert(person1.friends); //"Shelby,Count,Van"alert(person2.friends); //"Shelby,Count"alert(person1.friends === person2.friends); //falsealert(person1.sayName === person2.sayName); //true
組合使用建構函式模式和原型模式中建構函式模式用於定義執行個體屬性,而原型模式用於定義方法和共用的屬性。
其他模式的缺點:
原廠模式:雖然解決了建立多個相似對象的問題,但卻沒有解決對象識別的問題(即怎樣知道一個對象的類型);
建構函式模式:每個方法都要在每個執行個體上重新建立一遍;
原型模式:原型中所有屬性是被很多執行個體共用的,這種共用對於函數非常合適,然而對於包含參考型別值的屬性來說問題比較大。
5、JavaScript中的繼承可以使用組合繼承(也叫偽經典繼承):
function SuperType(name){ this.name = name; this.colors = ["red", "blue", "green"];}SuperType.prototype.sayName = function(){ alert(this.name);};function SubType(name, age){ //繼承屬性 SuperType.call(this, name); this.age = age;}//繼承方法SubType.prototype = new SuperType();SubType.prototype.constructor = SubType;SubType.prototype.sayAge = function(){ alert(this.age);};var instance1 = new SubType("Nicholas", 29);instance1.colors.push("black");alert(instance1.colors); //"red,blue,green,black"instance1.sayName(); //"Nicholas";instance1.sayAge(); //29var instance2 = new SubType("Greg", 27);alert(instance2.colors); //"red,blue,green"instance2.sayName(); //"Greg";instance2.sayAge(); //27
組合繼承使用原型鏈實現對原型屬性和方法的繼承,通過借用建構函式來實現對執行個體屬性的繼承,從而避免了原型鏈和借用建構函式的缺陷。但是組合繼承也有不足,即無論什麼情況下,都會調用兩次超類型建構函式:一次是在建立子類型原型的時候(new SuperType()),另一次是在子類型建構函式內部(SuperType.call(this, name)),寄生組合式繼承克服了這個缺點,基本模式如下:
function object(o){ function F(){} F.prototype = o; return new F();}function inheritPrototype(subType, superType){ var prototype = object(superType.prototype); //建立對象 prototype.constructor = subType; //增強對象 subType.prototype = prototype; //指定對象}
所謂寄生組合式繼承,即通過借用建構函式來繼承屬性,通過原型鏈的混成形式來繼承方法。其背後的基本思路是:不必為了指定子類型的原型而調用超類型的建構函式,我們所需要的無非就是超類型原型的一個副本而已。本質上,就是使用寄生式繼承來繼承超類型的原型,然後再將結果指定給子類型的原型。我們就可以用調用 inheritPrototype()函數的語句,去替換前面例子中為子類型原型賦值的語句,例如:
function SuperType(name){ this.name = name; this.colors = ["red", "blue", "green"];}SuperType.prototype.sayName = function(){ alert(this.name);};function SubType(name, age){ SuperType.call(this, name); this.age = age;}inheritPrototype(SubType, SuperType);SubType.prototype.sayAge = function(){ alert(this.age);};
6、由於耦合的問題,在編寫遞迴調用時,使用 arguments.callee 總比使用函數名更保險:
function factorial(num){ if (num <= 1){ return 1; } else { return num * arguments.callee(num-1); //不要使用return num * factorial(num-1); }}
7、由於閉包會攜帶包含它的函數的範圍,因此會比其他函數佔用更多的記憶體,過度使用閉包可能會導致記憶體佔用過多。
8、由於 JavaScript 沒有塊級範圍,因此可以使用匿名函數來模仿塊級範圍:
(function(){ //這裡是塊級範圍})();
這種做法可以減少閉包佔用的記憶體問題,因為沒有指向匿名函數的引用。這種技術也經常在全域範圍中被用在函數外部,從而限制向全域範圍中添加過多的變數和函數。
9、盡量不要使用間歇調用,因為在不加幹涉的情況下,間歇調用將會一直執行到頁面卸載,而且後一個間歇調用可能會在前一個間歇調用結束之前啟動,最好使用逾時調用來類比間歇調用,對比如下兩段代碼:
間歇調用:var num = 0;var max = 10;var intervalId = null;function incrementNumber() { num++; //如果執行次數達到了 max 設定的值,則取消後續尚未執行的調用 if (num == max) { clearInterval(intervalId); alert("Done"); }}intervalId = setInterval(incrementNumber, 500);
逾時調用類比間歇調用:var num = 0;var max = 10;function incrementNumber() { num++; //如果執行次數未達到 max 設定的值,則設定另一次逾時調用 if (num < max) { setTimeout(incrementNumber, 500); } else { alert("Done"); }}setTimeout(incrementNumber, 500);
10、為了確保跨瀏覽器安全色,最好還是將 nodeType 屬性與數字值進行比較,因為IE無法訪問 Node 類型。
11、使用cloneNode()方法時在複製之前最好先移除事件處理常式,因為IE 在此存在一個 bug,它會複製事件處理常式。
12、盡量減少訪問 NodeList 的次數。因為每次訪問 NodeList,都會運行一次基於文檔的查詢。
13、由於老版本的瀏覽器不支援,因此在有特殊需要時再使用事件捕獲,可以放心地使用事件冒泡。
14、因為HTML 與 JavaScript 代碼緊密耦合,因此不要使用 HTML 事件處理常式,可以使用 JavaScript 指定事件處理常式。
15、使用事件委託,在DOM 樹中盡量最高的層次上添加一個事件處理常式,不必給每個可單擊的元素分別添加事件處理常式,因為事件委託利用了事件冒泡,只指定一個事件處理常式,就可以管理某一類型的所有事件。最適合採用事件委託技術的事件包括 click、mousedown、mouseup、keydown、keyup 和 keypress。
16、調用 submit()方法的形式提交表單時,不會觸發 submit 事件,因此要記得在調用此方法之前先驗證表單資料。與調用 submit()方法不同,調用 reset()方法會像單擊重設按鈕一樣觸發 reset 事件。
17、讀取或設定文字框的值時不建議使用標準的 DOM 方法,而是使用 value 屬性:
var textbox = document.forms[0].elements["textbox1"];textbox.value = "Some new value";
換句話說,不要使用 setAttribute()設定<input>元素的 value 特性,也不要去修改<textarea>元素的第一個子節點。原因很簡單:對 value 屬性所作的修改,不一定會反映在 DOM 中。因此,在處理文字框的值時,最好不要使用 DOM 方法。
18、不建議使用常規的 DOM 功能來訪問 option 元素的資料,因為效率比較低,最好是使用特定於選項的屬性,因為所有瀏覽器都支援這些屬性:
var selectbox = document.forms[0].elements["location"];//不推薦var text = selectbox.options[0].firstChild.nodeValue; //選項的文本var value = selectbox.options[0].getAttribute("value"); //選項的值//推薦var text = selectbox.options[0].text; //選項的文本var value = selectbox.options[0].value; //選項的值
JavaScript 基礎最佳化(《JavaScript 進階程式設計》讀書筆記)