剛開始學習設計模式以前,我是沒想說要學習設計模式的,我只是由於想學習JS中的原型prototype知識,一開始我想JS中爲何要存在原型這個東西?因而慢慢經過原型而接觸到設計模式,後來發現我這個過程是很是正確的,即先學習設計模式,而後在剖析原型及其原理。
設計模式
我一開始,都是經過針對原型的知識點去看去學,發現仍是理解不了,就算理解了原型,能夠說只是停留在表面的知道,你並不懂得如何運用原型?後來,我經過用設計模式的角度去理解原型prototype,我才煥然大悟!數組
可是在這一篇章裏面,並無說到原型、原型模式,由於若是你要進入設計模式的角度去理解原型,你就必須得了解一下用JS來實現單例模式、工廠模式和構造函數模式,看看Javascript是若是巧妙地實現各類設計模式。瀏覽器
【單例模式】模塊化
var person1 = { "name": "psg", "age": 24 }; var person2 = { "name": "fsf", "age": 22 }; console.log(person1.name); // ->page console.log(person1.age); // ->24 console.log(person2 .name); // ->fsf console.log(person2 .age); // ->22
上面的person1與person2都不是基本數據類型,都是對象數據類型,對象數據類型的做用:把描述同一個事物(同一個對象)的屬性和方法放在一個內存空間下,起到了分組的做用(例如person1與person2,都是兩個獨立開來的棧內存,裏面的屬性和方法互不影響) -> 因而,咱們把這種分組編寫代碼的模式叫作「單例模式」。函數
-> 在「單例模式」中,咱們叫person一、person2叫作命名空間學習
單例模式是一種項目開發中常用的模式,由於項目中咱們可使用單例模式進行「模塊化」開發。this
命名空間的做用,看下面代碼spa
// searchRender 是一個命名空間 var searchRender = { change: function() { this.clickEvent(); //這裏this你暫時看不出來是誰,由於只有執行的時候,才知道 } , clickEvent: function() { } }; // 可是想要使用searchRender裏面的change方法,你必須以下調用 serachRender.clickEvent(); // 因此change方法裏面的this,只能是searchRender
若是你學的比較深刻,看上面的代碼你就能看出命名空間的做用是什麼了,就是能靈活運用this。prototype
這裏使用this的好處:當命名空間更名字的時候,你並不用改裏面的調用者,由於this就表明命名空間。設計
總結
->單例模式解決了分組的問題
->可是單例模式的弊端也很明顯,由於他仍然是手工做業模式,效率比較低,因而「工廠模式」就站出來解決了這個問題,由於工廠模式能夠批量生產。
【工廠模式】
function createJsPerson(name, age) { var obj = {}; obj.name = name; obj.age = age; obj.writeJs = function() { console.log("My name is " + this.name + ", i can write js."); }; return obj; } var p1 = createJsPerson("psg", 24); var p2 = createJsPerson("fsf", 22) p1.writeJs(); // ->My name is psg, i can write js. p2.writeJs(); // ->My name is fsf, i can write js.
這樣寫,能夠減小重複代碼的產出,你只須要寫好公共的代碼,把他放進一個方法裏面,而後經過接受不一樣的參數,能夠建立不一樣的對象,效率比單例模式高多了!
接下來,講一下「構造函數模式」,它的樣子跟「工廠模式」很是類似
【構造函數模式】
function CreateJsPerson(name, age) { // var obj = {}; this.name = name; this.age = age; this.writeJs = function() { console.log("My name is " + this.name + ", i can write js."); } // return obj; } var p = new CreateJsPerson("psg", 24); p.writeJs(); // -> My name is psg, i can write js.
看到沒有,CreateJsPerson這個方法,根本不會像工廠模式那樣最後返回給你一個對象,它不須要,由於當你經過使用new關鍵字,瀏覽器會在後臺中本身新建一個對象,而後經過this來指向該新建對象,最後自動默認返回一個對象給你。
->其實咱們有時也會不經意用到使用構造函數模式新進實例,例如建立一個數組
// 建立一個數組 var ary = []; // 字面量方式 var arry = new Array(); // 實例建立的方式-》構造函數模式執行的方式
->知識點一: 構造函數模式和工廠模式的區別?
1)執行的時候
普通函數執行 -> createJsPerson() -> 這時候,createJsPerson是一個普通函數名
構造函數模式 -> new CreateJsPerson() -> 經過new執行後,CreateJsPerson就是一個類 -> 開頭大寫是由於約定了,大寫開頭的函數就是表示一個類
new 出來的返回值(p),就是CreateJsPerson這個類的一個實例
2)在函數執行的時候
相同點 -> 都是造成私有做用域,而後形參賦值 -> 預解釋 -> 代碼從上到下執行(類和普通函數同樣,它也有普通函數的一面)
不一樣點 -> 在代碼執行以前, 不用本身再手動建立對象,瀏覽器會默認的建立一個對象數據類型的值(類的實例),並做爲函數返回值自動返回。
->知識點二: 構造函數也是函數數據類型
在JS中,全部類都是函數數據類型,createJsPerson是函數數據類型, new CreateJsPerson也是函數數據類型,CreateJsPerson它經過new執行變成了一類,但它自己也是一個普通的函數。
可是,JS中全部類的實例都是對象數據類型。
->知識點三: 若是給構造函數裏面添加私有變量,它返回值(實例)會存在此變量嗎?
unction Fn() { var num = 10; } var obj = new Fn; console.log(num); // -> undefined console.log(obj.num); // -> undefined
上面例子能夠看出,這裏的num只是函數私有做用域裏面的一個私有變量,它跟實例沒有任何關係。
->知識點四: 若是給構造函數直接返回一個對象,或者直接返回一個基本數據類型,那個實例到底仍是不是瀏覽器默認返回的實例呢?
1)手動添加自動返回基本數據類型
function Fn() { var num = 10; return 100; // 代碼無效 } var f1 = new Fn; console.log(f1); // -> Object,這個Object就是空的,也是瀏覽器默認返回的對象 // 也就是說,return 100 這句代碼無效
2)手動添加自動返回對象數據類型
function Fn() { var num = 10; return {name: 'psg'}; // 代碼有效 } var f1 = new Fn; console.log(f1); // -> Object,這個Object就是 {name: 'psg'} // 也就是說,強制返回的手動添加的實例對象,徹底KO覆蓋掉瀏覽器默認返回的實例對象
總結上面兩點在構造函數模式當中,瀏覽器會默認把咱們的實例返回(返回的是一個對象數據類型的值)
// 可是若是咱們手動返回值,分兩種狀況
// ******狀況一,返回的是一個基本數據類型的值,當前實例不變
// 例如: return 100
// 那麼瀏覽器返回的值仍然是瀏覽器默認建立的對象
// ******狀況二,返回的是一個引用數據類型的值,當前實例會被本身返回的值覆蓋掉
// 例如: return {name: "psg"}
// 那麼原先瀏覽器返回的默認值,將會被本身的手動建立返回的對象給覆蓋掉。注意,必定要是對象哦
->知識點五: 檢測某一個實例是否屬於這個類 -> instanceof
下面這個例子頗有意思!!結合了知識點四。
function Fn() { var num = 10; // return 100 return {name: 'psg'} } var f1 = new Fn; console.log(f1 instanceof Fn); // ->false , 由於f1不是瀏覽器建立的默認實例
->知識點六:檢測某個屬性是否屬於某個實例,檢測某個屬性是否爲該對象的公有屬性?
function Fn1() { this.x = 100; this.getX = function() { console.log(this.x); } } var fun1 = new Fn1; var fun2 = new Fn1; console.log(fun1.getX() === fun2.getX()); // -> fun1和fun2都是Fn1這個類的實例,都擁有x和getX這兩個屬性,可是這兩個屬性是各自私有的屬性 // 可是如何檢測某一個屬性是否屬於某一個實例 // in: 檢測某一個屬性是否屬於某一個實例,不論是私有屬性仍是公有屬性,用in都是用來檢測這個屬性是否屬於這個對象 console.log("getX" in fun1); // -> true // hasOwnProperty: 用來檢測某個屬性是否爲這個對象的私有屬性,這個方法只能檢測私有的屬性 console.log(fun1.hasOwnProperty("getX")); // -> true // 思考,檢測某一個屬性是否爲該對象的「公有屬性」,本身寫一個 hasPubProperty function hasPubProperty(obj, attr) { // 首先保證它是一個對象的屬性,而且不是私有屬性,那就確定就是公有屬性了 return ((attr in obj) && !obj.hasOwnProperty(attr)); } console.log(hasPubProperty(fun1, "getX")); // -> false;
這一章,主要講了單例模式、工廠模式和構造函數模式,同時也由於工廠模式與構造函數模式代碼上很是類似,可是實現原理徹底不相同,因此也講了構造函數模式實例化對象的原理過程,以及他們兩的區別。
下一章 的《【設計模式+原型理解】第二章:JS中爲何要存在原型prototype這個東西?》會講到原型模式,準確來講是基於構造函數模式的原型模式,由於只有講到構造函數模式,你才知道構造函數的優缺點,原型模式就是爲了解決並改進構造函數的。
待續....