js編碼中的一些細節
來自: https://segmentfault.com/a/1190000004484977
我們認為,JavaScript的正確運行不應該依賴CSS-在缺少CSS情況下也要能夠正確運行,盡管兩者之間可能會有互動。
</div>
一、松耦合的一些建議
當你能夠做到修改一個組件而不需要更改其它組件的時候,你就做到了松耦合。
1、將JavaScript從CSS中抽出來(現在基本不會有如下的寫法)
.box{width: expression(document.body.offsetWidth + "px")} //不好的寫法
2、將CSS從JavaScript中抽離,利用增減class類名來控制樣式
element.style.color = "red"; //不好的寫法
3、將JavaScript從HTML抽離
<!-- 不好的寫法 --> <button onClick="doSomething()" id="action-btn">Click me</button>
上面的寫法在兩個UI層的深耦合,可能會出現用戶點擊的時候函數不存在的情形。或者,修改了 doSomething 之后,可能需要同時修改 JavaScript 和 HTML 代碼。利用DOM二級事件綁定時是更好的處理方法。
最好將所有的JavaScript代碼都放入外置文件中,以確保HTML代碼中不會有內聯的JavaScript代碼
4、 將HTML從JavaScript中抽離
通常代碼中會出現這樣的寫法:
//不好的寫法
var div = document.getElementById("my-div");
div.innerHTML = "<h3>Error</h3><p>invalid e-mail address</p>";
解決方法:
-
通過ajax,從服務器加載
-
利用客戶端模板
二、關于變量的一些建議
全局變量所帶來的問題:
-
命名沖突
-
任何函數不經意間會修改全局變量,從而造成某處代碼出錯
-
難以測試
關于全局變量的一些解決tip:
-
所有變量都用var來定義,防止js不小心生成全局變量
-
避免意外的全局變量,利用jsLint來報警。推薦編輯器Bracket
-
使用嚴格模式來檢驗
-
使用單全局變量方式(單例模式)
三、事件處理的松耦合處理
1、隔離應用層邏輯
//不好的寫法
function handleClick(event){
var popup = document.getElementById("popup");
popup.style.left = event.clientX + "px";
popup.style.top = event.clientY + "px";
}
addEventListener(element, "click", handleClick);</pre>
上面的一段代碼看上去沒有任何的問題,但是邏輯層跟應用層耦合了。如果說其他的事件處理程序執行了同樣的程序。這樣多個事件的處理程序執行了同樣的邏輯,而你的代碼卻被不小心復制了多份。
修改之后:
//拆分應用層邏輯
var MyApplication = {
handleClick:function(event){
this.showPopup(event);
},
showPopup:function(event){
var popup = document.getElementById("popup");
popup.style.left = event.clientX + "px";
popup.style.top = event.clientY + "px";
}
}
addEventListener(element, "click", function(){
MyApplication.handleClick(event);
});</pre>
看似多寫了幾行代碼,但更加好維護
2、不要分發事件對象e
上面代碼中定義的方法showPopup,每次調用需要傳入event對象,但是它只是用到了event其中的兩個屬性,其實可以通過值給其兩個明確的參數來改進代碼
//好的寫法
var MyApplication = {
handleClick:function(event){
this.showPopup(event.clientX,event.clientY);
},
showPopup:function(x, y){
var popup = document.getElementById("popup");
popup.style.left = x + "px";
popup.style.top = y + "px";
}
}
addEventListener(element, "click", function(){
MyApplication.handleClick(event);
});</pre>
上面的代碼其實還有一個點可以優化:事件處理程序應當在進入應用邏輯之前針對event對象執行任何必要的操作,包括阻止默認事件或阻止事件冒泡都應當直接包含在事件處理程序中。如下:
var MyApplication = {
handleClick:function(event){
//假設事件支持DOM level2
event.preventDefault();
event.stopPropagation();
//傳入應用層邏輯
this.showPopup(event.clientX,event.clientY);
},
showPopup:function(x, y){
var popup = document.getElementById("popup");
popup.style.left = x + "px";
popup.style.top = y + "px";
}
}
addEventListener(element, "click", function(){
MyApplication.handleClick(event);
});</pre>
四、判斷語句中注意避免空比較
復習一下知識,檢測各類值的操作
1、檢測原始值(字符串、數字、布爾值、undefined):用 typeof
2、檢測null:用 === 或 !== 來進行判斷 (typeof null = "object")
3、檢測引用值: instanceof 運算符
//檢測日期
if(value instanceof Date){
console.log('1')
}
4、檢測函數: console.log(typeof myFunc === "function")
5、檢測數組:
-
鴨式辯型(具體解釋可以見犀牛書)
function isArray(value){
return typeof value.sort === "function"
}
-
Kangax解決方法
</ul>
function isArray(value){
return Object.prototype.toString.call(value) === '[object Array]';
}
-
ES5方法: Array.isArray
</ul>
6、檢測屬性我曾經寫過
//檢測假值
if(a[b]){...}
//與null相比較
if(a[b] != null){...}
//與undefined相比較
if(a[b] != undefined){...}</pre>
但是上面的寫法都不夠明確,都是通過給定的名字來檢查屬性的值,而非給定的名字所指的屬性是否存在,因此當屬性值為 0,false,null,undefined 會出錯
推薦下面的寫法: in 運算符+ hasOwnProperty
五、配置數據分離
配置數據:應用中寫死的值
-
URL
-
需要展現給用戶的字符串
-
重復的值
-
設置(比如每頁的配置項)
-
任何可能發生變化的值
處理方式:
1、抽離配置數據,用單獨的變量存放,方便修改
2、保存配置數據:值得嘗試,將配置數據存放在非JavaScript文件中
-
JSON
-
JSONP
-
純JavaScript
六、錯誤拋出的方法
1、拋出錯誤的方法: throw new Error('Something has happened')
2、拋出錯誤的經驗法則:
-
一旦修復了一個很難調試的錯誤,嘗試增加一個兩個自定義錯誤。當再次發生錯誤時,這將有助于更容易地解決問題
-
如果正在編寫代碼,思考一下:“我不希望代碼拋出某種錯誤”。這時,如果“某種錯誤發生”,就拋出一個錯誤
-
如果正在編寫的代碼別人也會使用,思考一下他們使用的方式,在特定的情況下拋出錯誤
3、try···catch語句4、處理具體的錯誤類型
try {
//有些代碼引發了錯誤
} catch (ex) {
if(ex instanceof TypeError) {
//處理TypeError錯誤
} else if(ex instanceof ReferenceError){
//處理Reference錯誤
} else {
//其它處理
}
}
七、針對對象的處理
1、基于對象的繼承
var person = {
name:'a',
sayName:function(){
alert(this.name);
}
}
var myPerson = Object.create(person);
myPerson.sayName(); //彈出a</pre>
如果重新定義sayName方法,會自動切斷對person.sayName()的方法
myPerson.sayName = function(){
alert("b");
}
myPerson.sayName();//彈出b
person.sayName();//彈出a
Object.create() 方法可以指定第二個參數,該參數對象中的屬性和方法將添加到新的對象中
var myPerson = Object.create(person,{
name:{
value:'Ge'
}
});
myPerson.sayName();//彈出Ge
person.sayName();//彈出a</pre>
3、基于類型的繼承(兩步)
-
原型繼承
-
構造器繼承
function Person(name){
this.name;//屬性name實際上是由Person類管理
}
function Author(name){
//繼承構造器
Person.call(this,name);//允許Person構造器繼續定義該屬性。Person構造器是在this上執行的,this指向一個Author對象,所以最終的name定義在這個Author對象上面
}
Author.prototype = new Person();</pre>
八、瀏覽器嗅探
1、user-Agent檢測
//不好的做法但是是通常的做法
if(navigator.userAgent.indexOf("MSIE") > -1) {
//是Internet Explorer
} else {
//不是Internet Explorer
}
瀏覽器的發展使得字符串檢測的用戶體驗越來越差,最佳的方法是只檢測舊的瀏覽器,比如IE8和之前的版本而不要試圖檢測IE9和更高版本
if( isInternetExplorer8OrEalier ){
//處理IE8以及更早版本
} else {
//處理其它瀏覽器
}
參考資料:《編寫可維護的JavaScript》
</div>