【設計模式+原型理解】第一章:使用Javascript來巧妙實現經典的設計模式

        剛開始學習設計模式以前,我是沒想說要學習設計模式的,我只是由於想學習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這個東西?》會講到原型模式,準確來講是基於構造函數模式的原型模式,由於只有講到構造函數模式,你才知道構造函數的優缺點,原型模式就是爲了解決並改進構造函數的。

       待續....

相關文章
相關標籤/搜索