【設計模式+原型理解】第二章:基於構造函數擴展出來的原型模式

       在第一章的時候,說過了單例模式、工廠模式、構造函數模式,你還記得構造模式是怎麼樣的嗎?瀏覽器

function CreateJsPerson(name, age) {
	this.name = name;
	this.age = age;
	this.writeJs = function() {
		console.log("my name is " + this.name + ", i can write js.");
	}
}
var p1 = new CreateJsPerson("p1", 18);
var p2 = new CreateJsPerson("p2", 17);
p1.writeJs();
p2.writeJs();
console.log(p1.writeJs() === p2.writeJs()); //-> false
// 構造函數模式中擁有了類和實例的概念,而且實例和實例之間是相互獨立開的
// -> 叫作實例識別

       上面的就是構造函數模式,你知道,爲何p一、p2的writeJs()方法爲何不相等嗎?這是由於正如上面所說的,兩個實例是相互獨立的,也就是說,兩個實例的屬性都是各自私有屬性。函數

   -> 問題來了,兩個實例裏面的屬性都是私有的之外,是否是還得有公有的部分?學習

 

1、【基於構造函數的原型模式this

        使用基於構造函數模式的原型模式,可以實現把writeJs()方法變成公有的,代碼以下:spa

function CreateJsPerson(name, age) {
	this.name = name;
	this.age = age;
	// this.writeJs = function() {
	// 	 console.log("my name is " + this.name + ", i can write js.");
	// }
}
CreateJsPerson.prototype.writeJsG = function() {
	console.log("my name is " + this.name + ", i can write js.");
};

var p1 = new CreateJsPerson("p1", 18);
var p2 = new CreateJsPerson("p2", 17);
p1.writeJs();
p2.writeJs();
console.log(p1.writeJsG() === p2.writeJsG()); //->true

 

        爲何這樣寫,就能把屬性變爲公有的呢?prototype

        基於構造函數模式擴展出來的原型模式:對象

                ->它解決了方法或者屬性公有的問題blog

                ->即把實例之間公有的屬性和方法提出成公有的屬性和方法繼承

                ->想讓誰公有,就把它放在prototype上便可內存

 

2、【原型基礎3句話】

         想要學習原型,要記住下面的三句話(不要問爲何~~):

         1)每個函數數據類型(普通函數、類)都有一個天生自帶的屬性,prototype(原型),而且這個屬性是一個對象數據類型的值。

          2)而且在prototype上瀏覽器會天生給它加上一個屬性contructor(構造函數),屬性值是當前函數(類)自己。

          3)每個對象數據類型(普通對象,實例,prototype...)也天生自帶一個屬性:__proto__,屬性值是當前實例所屬類的原型(prototype)。

下面這段代碼,能夠說明一下上面的3句話:

function Fn() {
	this.x = 100;
};
Fn.prototype.getX = function() {
	console.log(this.x);
};
var f1 = new Fn;
var f2 = new Fn;
console.log(Fn.prototype.constructor === Fn); //->true
// 堆內存:存儲 對象、函數裏面的代碼字符串

 

上面代碼的原型鏈以下圖所示,正方形表明棧內存(即函數做用域),橢圓正方形表明堆內存(即對象)

 

    從畫圖能夠很清晰看到,整個基於構造函數擴展出來的原型鏈函數,類與實例的原型鏈,實例與JS基類Object的原型鏈關係,一覽無遺。

    上圖+代碼,能夠總結出:

     一、Object是JS中全部對象數據類型的基類(最頂層的類)

           1) f1 instanceof Object -> true,這是由於f1經過__proto__能夠向上級查找,無論有多少級,最後總能找到Object

            2)在Object.prototype上沒有__proto__這個屬性(由於本身指向本身沒意義)

      二、原型鏈模

            舉個簡單的例子,f1.hasOwnProperty("x");  //->hasWwnProperty是f1的一個屬性,可是咱們發現f1的私有屬性上並無這個方法,那如何處理的呢?

            經過 對象名.屬性名 的方式獲取屬性值的時候,首先在對象的私有的屬性上進行查找,若是私有中存在這個屬性,則獲取的是私有的屬性值;

         ->若是私有的沒有,則經過__proto__找到所屬類的原型(類的原型上定義的屬性和方法都是當前實例的公有的屬性和方法),原型上存在的話,獲取的是公有的屬性值;

        -> 若是原型上也沒有,則繼續經過原型上的__proto__繼續向上查找,一直找到Object.prototype爲止...

         ->這種查找的機制就是咱們的原型鏈模式

 

3、【原型知識玩起來】

cconsole.log(f1.getX === f2.getX); //->true
console.log(f1.__proto__.getX === f2.getX); //->true 
console.log(f1.getX === Fn.prototype.getX); //->true
// f1.getX 跟 f1.__proto__.getX的區別
// 前者是瀏覽器先找私有做用域,找不到再找公有做用域
// 後者是瀏覽器直接查找公有做用域
// 
console.log(f1.hasOwnProperty === f1.__proto__.__proto__.hasOwnProperty);

// 在IE瀏覽器中,咱們原型模式也是一樣的原理,可是IE瀏覽器怕你經過__proto__把公有的修改,
// 禁止咱們使用__proto__,下面的例子就能夠很明顯的說明爲啥IE禁止了

f1.sum == function() {
	//修改本身私有的sum
};
f1.__proto__.sum = function() {
	//修改所屬類原型上的sum
};
// 因此修改公有的,IE只能經過prototype
Fn.prototype.sum = function() {
	// 修改公有的sum
};

 

4、【總結】

        在這一章中,主要說的是原型模式,可是原型模式是經過構造函數擴展出來的,同時也經過代碼+圖的方式,把原型模式的實現原理給畫了出來。

        既然原型這個知識點出來了,我會在後面介紹一下,使用原型來實現類的封裝、繼承、多態(裏面的重寫),而且會介紹使用原型來實現7中繼承方法,將會很是有趣。

相關文章
相關標籤/搜索