編寫高質量javascript代碼的基本要點
一、最小全局變量
二、函數做用域和聲明提早
三、命名規範javascript
函數聲明:
function 函數名稱 (參數:可選){ 函數體 }
函數表達式:
function 函數名稱(可選)(參數:可選){ 函數體 }
函數聲明只能出如今程序或函數體內html
var f = function foo(){
return typeof foo; // foo是在內部做用域內有效
};
// foo在外部用因而不可見的
typeof foo; // "undefined"
f(); // "function"
JScript的Bug 關於ie的
詳細內容參看
JScript的內存管理java
var f = (function(){
if (true) {
return function g(){};
}
return function g(){};
})();
釋放空間是必須的ajax
var f = (function(){
var f, g;
if (true) {
f = function g(){};
}
else {
f = function g(){};
}
// 設置g爲null之後它就不會再佔內存了
g = null;
return f;
})();
var hasClassNameFun = (function(ement,cname){
// 定義私有變量
var cache = { };
// 使用函數聲明
function hasClassName(element, className) {
var _className = '(?:^|\\s+)' + className + '(?:\\s+|$)';
var re = cache[_className] || (cache[_className] = new RegExp(_className));
return re.test(element.className);
}
// 返回函數
return hasClassName;
})();
其實返回一個函數,而不是函數表達式,在應用上會好一些,代碼簡單明白。
在應用嚴格模式的時候,會大量用到命名函數表達式,理解命名函數表達式的語義及其bug顯得更加劇要了。編程
// 若是你不在乎返回值,或者不怕難以閱讀
// 你甚至能夠在function前面加一元操做符號閉包
function foo(){ /* code */ }( 1 );
自執行函數格式能夠簡單寫成這樣框架
!function () { /* code */ } ();
~function () { /* code */ } ();
-function () { /* code */ } ();
+function () { /* code */ } ();
The Single Responsibility Principle(單一職責SRP)
The Open/Closed Principle(開閉原則OCP)
The Liskov Substitution Principle(里氏替換原則LSP)
The Interface Segregation Principle(接口分離原則ISP)
The Dependency Inversion Principle(依賴反轉原則DIP)
一、單一職責的描述以下:
類發生更改的緣由應該只有一個異步
遵照單一職責的好處是可讓咱們很容易地來維護這個對象,當一個對象封裝了不少職責的話,一旦一個職責須要修改,勢必會影響該對象想的其它職責代碼。經過解耦可讓每一個職責工更加有彈性地變化。模塊化
二、開閉原則的描述是:函數
軟件實體(類,模塊,方法等等)應當對擴展開放,對修改關閉,即軟件實體應當在不修改的前提下擴展。
三、里氏替換原則的描述是:
派生類型必須能夠替換它的基類型
在面向對象編程裏,繼承提供了一個機制讓子類和共享基類的代碼,這是經過在基類型裏封裝通用的數據和行爲來實現的,而後已經及類型來聲明更詳細的子類型,爲了應用里氏替換原則,繼承子類型須要在語義上等價於基類型裏的指望行爲。
四、接口隔離原則的描述是:
不該該強迫客戶依賴於它們不用的方法。
當用戶依賴的接口方法即使只被別的用戶使用而本身不用,那它也得實現這些接口,換而言之,一個用戶依賴了未使用但被其餘用戶使用的接口,當其餘用戶修改該接口時,依賴該接口的全部用戶都將受到影響。這顯然違反了開閉原則,也不是咱們所指望的。
接口隔離原則ISP和單一職責有點相似,都是用於彙集功能職責的,實際上ISP能夠被理解才具備單一職責的程序轉化到一個具備公共接口的對象。
5.依賴倒置原則的描述是:
高層模塊不該該依賴於低層模塊,兩者都應該依賴於抽象
抽象不該該依賴於細節,細節應該依賴於抽象
依賴倒置原則的最重要問題就是確保應用程序或框架的主要組件從非重要的底層組件實現細節解耦出來,這將確保程序的最重要的部分不會由於低層次組件的變化修改而受影響。
在JavaScript裏,單例做爲一個命名空間提供者,從全局命名空間裏提供一個惟一的訪問點來訪問該對象。
它就是一個簡單的有一些方法和屬性的對象組成的,能夠稱爲命名空間
var Singleton={
attr:1,
another_attr:‘value’,
method:function(){...},
another_method:function(){...},
Util:{
util_method1: function(){...},
util_method2:function(){}
},
Ajax:{
ajax_method:function(){...}
},
some_method:function(){...}
};
能夠經過一個全局變量的入口訪問全部的方法和屬性。
Singleton.attr+=1;
Singleton.method();
Singleton.Util.util_method1();
Singleton.Ajax.ajax_method();
Singleton.some_method();
經過建立命名空間或者包來把代碼組織到邏輯塊中,當你經過使用命名空間把你的代碼從全局做用域移動到一個新的單例中的時候,能夠避免全局變量被意外覆蓋以及全局變量引發其餘的bug. 例如一些方法做爲全局函數,有可能會被覆蓋,尤爲是像get同樣簡單的名字。遇到這種狀況就把所有的亦是和函數添加到一個單例中,徹底不給別的代碼篡改你的代碼的機會。
若是之後要擴展該對象,你能夠添加本身的私有成員和方法,而後使用閉包在其內部封裝這些變量和函數聲明,只暴露你想暴露的public成員和方法,樣例代碼以下:
var mySingleton=function(){
var privateVariable="something private";
function showPrivate(){
console.log(privateVariable);
}
reutrn{
publicMethod:function(){
showPrivate();
},
publicVar:"the public can see this!"
};
}
var single=mySingleton();
single.publicMethod();
console.log(single.publicVar);
若是咱們想作到只有在使用的時候才初始化,爲了節約資源的目的,咱們能夠另一個構造函數裏來初始化這些代碼 :
var Singleton=(function(){
function Singleton(args){
var args=args||{};
this.name='SingletonTester';
this.pointX=args.pointX|| 6;
this.pointY=args.pointY||10;
}
var instance;
var _static={
name:'SingletonTester',
getInstance:function(args){
if(instance===undefined){
instance=new Singleton(args);
}
return instance;
}
};
return _static;
})();
var singletons=Singleton.getInstance({pointX:5});
console.log(singletons.pointX); //輸出5
javascript中’private’是做爲保留字,而不是關鍵字,javascript沒有私有化這樣的,一是定義變量的時候在前面加上下劃線」_」,第一種方法並非真正的私有,只是一種規範,若是要作到真正的私有,仍是要用第二種方法–閉包
(function(){
//此做用域的全部變量,函數依舊可在特定做用域中被訪問
})();
先用括號把函數定義括起來,從而獲得該函數對象,而後後面的括號是當即運行它,這種形式能夠在不少js庫中見到,例如jQuery:
js 代碼
(
function(window,undefined){
...
window.jQuery=window.$=jQuery;
}
)(window);
上面的代碼能夠看到是jQuery把window這個全局變量傳進匿名函數中,而後把內部定義的jQuery賦值給了window,從而在全局做用域中均可以經過」$」符號來訪問匿名函數中的內容。
javascript單體的模塊化編寫的基本樣子是下面的代碼:
var Module=(function(){
var my={}, privateVar=8;
function privateFun(){
return ++privateVar;
};
my.publicVar=1;
my.publicFun=function(){
return privateFun();
};
return my;
}());
console.log(Module.publicVar);//1
console.log(Module.publicFun());//9
在匿名函數中返回了一個my變量給Module做爲外部訪問閉包內容的接口,除閉包內my以外的內容都獲得了私有性保護,閉包的數據在Module變量的做用域中保持能夠訪問。
這種方法解決了javascript私有化的問題,咱們能夠利用它來定義命名空間,單例,擁有私有化封裝的對象等等,而後這種模式有其缺陷,例如:定義私有,公共變量的方法是不一樣的,當開發過程當中咱們須要改變某個變量可見性的時候,就不得不在它全部出現過的地方進行修改,而且javascript做爲動態編譯的語言,咱們能夠隨時給對象添加屬性,方法,然而咱們在閉包以外定義的方法是沒法直接訪問私有數據的。
上面的方法有一個限制,整個模板必須定義在一個文件中,曾面對一大堆代碼工做的人確定明白將它劃分爲多個文件的意義,好在,有個巧妙的方法來擴展咱們的Modules,首先,在匿名函數的參數中導入Module,而後給它添加屬性,而後再導出它,下面舉出在另外一個文件中對上面提到的Module進行擴展(必須是全局做用域的狀況)
js代碼
var Module=(function(my){
my.anotherFun=function(){
//do something...
};
return my;
}(Module||{})); //注意,這裏還有個「{}」
Module=(function(my){
my.workFun=function(){
//do something...
}
return my;
})(Module||{})
咱們從新用var關鍵字定義Module以保證一致性,而且若是以前Module沒有被建立的話,在這裏會自動的被建立爲空對象」{}」,當這段代碼運行結束以後,咱們的Module又有了一個新的公共方法Module.anotherFun(), 每一個些擴展的Module都有本身獨有的私有屬性,方法,因爲導入的時候能夠自動建立,這些包含Module定義的文件之間能夠任意順序加載(若是存在依賴關係 ,必須按次序加載的話,那你按次序就行了)。
目前的多文件擴展Module有一嚴重的限制,那就是如今文件 的Module都只保持本身的私有狀態,沒法訪問其餘文件的私有屬性,固然,這也能夠解決,下面是解決多文件私有屬性共享的一個方法。
var MODULE = (function () {
//將全部的私有屬性、方法都定義在_private對象中
//每一個擴展Module均可以經過my._private來訪問
var my = {},
_private = my._private = {},
_seal = function (){
//密封,刪除全部私有數據的可訪問性
delete my._private;
},
_unseal = function (){
//解封,讓私有數據從新可訪問
my._private = _private;
};
my.extend = function(otherModules){
//必須經過此方法來添加擴展Module文件
_unseal();
//add other modules
_seal();//異步調用,此處只是示意,真正的代碼並不是如此
}
return my;
}());