總的來講,javascript模式是一本力薦的js進階書,書裏面涉及了不少在學習javascript過程當中會碰到的坑,而後提供了很不錯的解決方法.雖然不少人吐槽這本書的翻譯,可是糟糕的翻譯仍是沒法掩蓋這是一本好書的事實.javascript
所以這裏我會結合書上的知識和個人理解來寫一些我認爲值得借鑑的內容.java
隨處可見的是使用new操做符的構造函數node
var People = function(){ this.type = "human"}; //構造函數 var aPerson = new People(); //用new操做符來實例化
因此當在new操做符下的構造函數究竟發生了什麼?數組
//new操做發生的事情 var this = Object.create(People.prototype); //建立新的對象,而後繼承自parent.prototype; this.type = "human"; //屬性和方法被加入到this下; return this; //若是構造函數沒有返回,則本身返回this;
如今有一個問題,若是用戶在使用構造函數的時候,漏掉了new操做符,那麼this就會被指向window/global上,從而出現錯誤;瀏覽器
var aPerson = People(); //這裏的this指向window console.log(aPerson.type); //undefined console.log(window.type); //'human'
若是漏掉了new操做符可能會致使比較嚴重的問題.所以也會有了所謂的安全模式:緩存
var People = function(cfg){ if(!(this instanceof People)){ //核心代碼 return new People(cfg); //作一個簡單的檢測 } .....//該幹嗎幹嗎 }
因此就算漏掉了new操做符,代碼的檢查機制也會幫你new,不會出現綁定的問題;安全
回調函數是解耦的神器,若是你的函數內部有一個部分會頻繁變化,能夠考慮把這些部分封裝到一個函數裏面做爲回調函數傳入,這樣就能夠解除不一樣函數之間的耦合,而且會方便修改;ide
var function complexFunction(){ function A(); //變化少的部分 function B(); //變化多的部分 function C() //變化少的部分 } //重構以後: var function complexFunction(fn){ function A(); fn(); function C(); } complexFunction(function B(){ //變化很大的部分 });
這樣就能夠把會常常變化的部分從不變的函數部分中解耦出來;並且書中的例子更好,一樣能夠學習參考下:函數
//原始代碼:第一個先在node裏面建立數據 var findNodes =function(){ //大概的做用是在循環裏面重複處理數據 var i = 100000, node = [], found; while(i){ i -= 1; node.push(found); } return nodes; } //這個函數的做用是循環數組來讓他消失;爲了解耦,因此分開了寫; var hide = function(nodes){ var i =0, max = nodes.length; for(;i<max;i+=1){ nodes[i].style.display = "none"; }; }; hide(findNodes());
這樣寫的問題在於作這個事情須要循環兩次,效率其實並不高;因此考慮用下面的方法重構:性能
//重構:提升效率和解耦 var findNodes = function(callback){ var i =100000, node=[], found; if( typeof callback !== "function"){ callback = false; //這裏單獨判斷是爲了放在循環以外 } while(i){ i -=1; //這裏執行很複雜的過程 if(callback){ callback(found); } nodes.push(found); } } var hide = function(node){ node.style.display = "none"; }; findNodes(hide);
因此經過回調函數的會讓函數解耦,特別是在循環體系以內;
若是一個函數有一些準備工做要作,可是這些工做也許只須要執行一次就行了,那麼你須要用自定義函數來提升性能;
var count = function(){ var number = 0; console.log("Init Done!") //完成只會執行一次的初始化 count = function(){ //這裏用新的函數覆蓋原函數 number++; console.log(number); } } Test: count(); //Init Done; count(); //1 count(); //2
因此完成了第一次的初始化,而後完成了一些初始化的工做.這裏能夠和下面的技巧一塊兒聯合使用;
若是你開頭就須要判斷一些東西,而後以後這個判斷都不須要執行,那麼你可能須要使用這種方法;
提及來比較抽象,可是這個比較常見,好比說瀏覽器須要知道你正在使用的瀏覽器,可是這個信息通常只會有一次判斷就能夠了,好比說下面的方法效率就很低;
var utils = { addListener:function(el,type,fn){ if(typeof window.addEventListener ==="function"){ el.addEventListener(type,fn,false); }else if(typeof document.attachEvent ==="function"){ // 判斷這是IE el.attachEvent("on"+type,fn); } } }
可是實際上這種信息我只須要判斷一次就好;可是若是寫在了utils裏面,那麼每次都須要執行;因此咱們能夠優化成爲:
var utils = { //這裏只是簡單的設置一個接口,實際的執行留在後面的初始化分支裏面 addListener:null }; // if(typeof window.addEventListener === "function"){ utils.addListener = function(type,fn,false){ el.addEventListener(type,fn,false); } }else(typeof document.attachEvent ==="function"){ utils.addListener = function(type,fn,false){ el.attachEvent("on"+type,fn); } }
函數是一個對象,那麼也就是能夠在屬性裏面添加一些信息.好比說數組的length,函數的name屬性就是原本就存在的;
那麼咱們也可使用這個特性來給某些計算開銷很大的函數,增長一個緩存,若是下次還須要計算這個值,就能夠直接返回緩存中的量;
var func = function(param){ if(!func.cache[param]){ var result = {}; //作一些很複雜的計算 func.cache[param] = result; } return func.cache[param]; }; myFunc.cache = {};
當你在作一個通用組件的時候,有時候你也須要留出必定的配置項出來讓用戶來設置;
var Widget = function(cfg){ cfg = cfg||{}; //若是傳入了就用,沒有的話就傳入{}; cfg.name && this.setName(cfg.name); cfg.theme && this.setTheme(cfg.theme); ......//一系列的配置項 }
大概的思路就是說若是用戶有傳入配置項,那麼就使用配置項.
以前學js的繼承的時候,一直很揪心的問題就是,到底什麼會繼承,什麼不會呢?這裏剛剛作一個總結
var Gadget = function(){ this.say1 = function(){ console.log("1~")}; var say2= function(){ console.log("2~")}; }; Gadget.prototype.say3 = function(){console.log("3")}; Gadget.say4 = function(){console.log("4")}; //This is a test; var gadget = new Gadget(); gadget.say1(); //1 經過this繼承方法效率不高,可是其實是可使用的 gadget.say3(); //3 最好的仍是經過prototype的方法繼承.正常訪問; Gadget.say2(); //報錯!在Gadget裏面設置的會沒法訪問; Gadget.say4();// 4,正常使用,所以經過屬性的方法添加到Gadget裏面的是能夠做爲Gadget靜態方法調用的