javascript設計模式

設計模式的定義:在面向對象軟件設計過程當中針對特定問題的簡潔而優雅的解決方案。javascript

人類能夠走到生物鏈頂端的前兩個緣由分別是會「使用名字」和「使用工具」。html

全部設計模式都遵循一條原則,即「找出程序中變化的地方,並將變化封裝起來」java

 API:(應用程序編程接口)是一些預先定義的函數,目的是提供應用程序與開發人員基於某軟件或某硬件得以訪問一組例程的能力,而又無需訪問源碼,或理解內部工做機制的細節。編程

 

絕大部分設計模式的實現都離不開多態性的思想,因此在學習設計模式以前先來了解一下多態小程序

 

一、靜態語言中的多態:設計模式

//多態

//這種方法沒有消除類型之間的耦合關係

/* 耦合:就是代碼關係過於緊密,每每改一小段代碼,須要整個項目作很大的改動。
 * 在實際開發中應該儘可能避免太高的耦合。接口、繼承是解耦合的一種好方法。
 */

class Duck {
	public void makeSound() {
		System.out.println("嘎嘎嘎");
	}
}

class Chicken {
	public void makeSound() {
		System.out.println("咯咯咯");
	}
}

class AnimalSound {
	public void makeSound(Duck duck) {
		duck.makeSound();
	}
	
//	public void makeSound(Chicken chicken) {
//		chicken.makeSound();
//	}
}

public class Test {
	public static void main(String[] args) {
		AnimalSound animalSound = new AnimalSound();
		Duck duck = new Duck();
		animalSound.makeSound(duck);
		
//		Chicken chicken = new Chicken();
//		animalSound.makeSound(chicken);
	}
}

  

二、靜態語言中,繼承實現多態效果:瀏覽器

// 繼承實現多態效果

/* 多態的思想其實是把「作什麼」和「誰去作」分離開來,要實現這一點,歸根結底先要消除類型之間的耦合關係。
 * 若是類型之間的耦合關係沒有被消除,那麼咱們在makeSound方法中指定了發出叫聲的對象是某個類型,它就不可能再被替換成另一個類型
 * 在java中,能夠經過向上轉型來實現多態*/

abstract class Animal {
	abstract void makeSound();  //抽象方法
}

class Duck extends Animal{
	public void makeSound() {
		System.out.println("嘎嘎嘎");
	}
}

class Chicken extends Animal{
	public void makeSound() {
		System.out.println("咯咯咯");
	}
}

class AnimalSound {
	public void makeSound(Animal animal) { //接受Animal類型的參數
		animal.makeSound();
	}
}

public class Test {
	public static void main(String[] args) {
		AnimalSound animalSound = new AnimalSound();
		Duck duck = new Duck();
		animalSound.makeSound(duck);
		
		Chicken chicken = new Chicken();
		animalSound.makeSound(chicken);
	}
}

  

三、JavaScript中的多態:函數

/*JavaScript的多態性是與生俱來的
		JavaScript做爲一門動態類型語言,它在編譯時沒有類型(包括對象類型和參數類型)檢查的過程,
		即不存在任何程度上的「類型耦合」,不須要向上轉型*/
		var googleMap = {
			show: function() {
				console.log('開始渲染谷歌地圖');
			}
		};

		var baiduMap = {
			show: function() {
				console.log('開始渲染百度地圖');
			}
		};

		// var sosoMap = {
		// 	show: function() {
		// 		console.log('開始渲染搜搜地圖');
		// 	}
		// };

		var renderMap = function(type) {
			if (type=='googleMap') {
				googleMap.show();
			}else if (type=='baiduMap') {
				baiduMap.show();
			}
			// else if (type=='sosoMap') {
			// 	sosoMap.show();
			// }
		};

		renderMap('googleMap');
		renderMap('baiduMap');
		// renderMap('sosoMap');

  

		var googleMap = {
			show: function() {
				console.log('開始渲染谷歌地圖');
			}
		};

		var baiduMap = {
			show: function() {
				console.log('開始渲染百度地圖');
			}
		};

		// var sosoMap = {
		// 	show: function() {
		// 		console.log('開始渲染搜搜地圖');
		// 	}
		// };

		var renderMap = function(type) {
			if (type.show instanceof Function) {
			//Function必須大寫,緣由能夠參考
			//連接1:https://www.cnblogs.com/shuiyi/p/5343399.html
			//連接2:http://www.cnblogs.com/shuiyi/p/5305435.html
			//連接3:https://zhidao.baidu.com/question/134033299.html?qbl=relate_question_5&word=function%20%BA%CDfunction
				type.show();
			}
		};

		renderMap(googleMap);
		renderMap(baiduMap);
		// renderMap(sosoMap);

  

 原型模式和基於原型繼承的 JavaScript對象系統 工具

一、使用克隆的原型模式學習

從設計模式的角度講,原型模式是用於建立對象的一種模式,若是咱們想要建立一個對象, 一種方法是先指定它的類型,而後經過類來建立這個對象。原型模式選擇了另一種方式,咱們 再也不關心對象的具體類型,而是找到一個對象,而後經過克隆來建立一個如出一轍的對象。 

原型模式的實現關鍵,是語言自己是否提供了clone方法。ECMAScript 5提供了Object.create 方法,能夠用來克隆對象。代碼以下: 

		var Plane = function() {
			this.blood = 100;
			this.attackLevel = 1;
			this.defenseLevel = 1;
		};

		var plane = new Plane();
		plane.blood = 500;
		plane.attackLevel = 10;
		plane.defenseLevel = 7;

		var clonePlane = Object.create(plane); //Object.create()方法能夠用來克隆對象
		// console.log(Plane);
		console.log(clonePlane);

		// 在不支持Object.create()的瀏覽器中,則可使用如下代碼:
		Object.create = Object.create || function(obj) {
			var F = function() {};
			F.prototype = obj;

			return new F();
		}

  

固然在 JavaScript這種類型模糊的語言中,建立對象很是容易,也不存在類型耦合的問題。 從設計模式的角度來說,原型模式的意義並不算大 。但 JavaScript自己是一門基於原型的面向對 象語言,它的對象系統就是使用原型模式來搭建的,在這裏稱之爲原型編程範型也許更合適。


二、體驗 Io語言 

事實上,使用原型模式來構造面向對象系統的語言遠非僅有 JavaScript一家。 JavaScript 基於原型的面向對象系統參考了 Self 語言 Smalltalk 語言,爲了搞清 JavaScript 中的原型,咱們本該尋根溯源去瞧瞧這兩門語言。但因爲這兩門語言距離如今實在太遙遠,咱們 不妨轉而瞭解一下另一種輕巧又基於原型的語言——Io語言。  Io語言在 2002年由 Steve Dekorte發明。能夠從http://iolanguage.com下載到 Io語言的解釋器, 安裝好以後打開 Io解釋器,輸入經典的「Hello World」程序。解釋器打印出了 Hello World的字符串,這說明咱們已經可使用 Io語言來編寫一些小程序了,以下圖所示。 

三、原型編程範型的一些規則 

跟使用「類」 的語言不同的地方是,Io語言中初只有一個根對象 Object,其餘全部的對象都克隆自另一 個對象。若是 A對象是從 B對象克隆而來的,那麼 B對象就是 A對象的原型。

在上一小節的例子中,Object 是 Animal 的原型,而 Animal 是 Dog 的原型,它們之間造成了一 條原型鏈。這個原型鏈是頗有用處的,當咱們嘗試調用 Dog 對象的某個方法時,而它自己卻沒有 這個方法,那麼 Dog 對象會把這個請求委託給它的原型 Animal 對象,若是 Animal 對象也沒有這個屬性,那麼請求會順着原型鏈繼續被委託給 Animal 對象的原型 Object 對象,這樣一來便能獲得繼承的效果,看起來就像 Animal 是 Dog 的「父類」,Object 是 Animal 的「父類」。 這個機制並不複雜,卻很是強大,Io和 JavaScript同樣,基於原型鏈的委託機制就是原型繼承的本質。

咱們來進行一些測試。在 Io的解釋器中執行 Dog makeSound 時,Dog 對象並無 makeSound 方 法,因而把請求委託給了它的原型 Animal 對象 ,而 Animal 對象是有 makeSound 方法的,因此該條 語句能夠順利獲得輸出,如圖 1-2所示。

咱們能夠發現原型編程範型至少包括如下基本規則。

 全部的數據都是對象。

 要獲得一個對象,不是經過實例化類,而是找到一個對象做爲原型並克隆它。 

 對象會記住它的原型。

 若是對象沒法響應某個請求,它會把這個請求委託給它本身的原型。

四、JavaScript中的原型繼承

咱們不能說在 JavaScript中全部的數據都是對象,但能夠說絕大部分數據都是對象。那麼相 信在 JavaScript中也必定會有一個根對象存在,這些對象追根溯源都來源於這個根對象。 事實上,JavaScript中的根對象是 Object.prototype 對象。Object.prototype 對象是一個空的對象。咱們在 JavaScript 遇到的每一個對象,實際上都是從 Object.prototype 對象克隆而來的, Object.prototype 對象就是它們的原型。好比下面的 obj1 對象和 obj2 對象:

var obj1 = new Object();
var obj2 = {};

能夠利用 ECMAScript 5提供的 Object.getPrototypeOf 來查看這兩個對象的原型:

console.log( Object.getPrototypeOf( obj1 ) === Object.prototype );    // 輸出:true
console.log( Object.getPrototypeOf( obj2 ) === Object.prototype ); // 輸出:true

  

五、準確的來講:若是對象沒法響應某個請求,它會把這個請求委託給它的構造器的原型

 

實際上,雖然 JavaScript 的對象最初都是由 Object.prototype 對象克隆而來的,但對象構造 器的原型並不只限於 Object.prototype 上,而是能夠動態指向其餘對象。這樣一來,當對象 a 需 要借用對象 b 的能力時,能夠有選擇性地把對象 a 的構造器的原型指向對象 b,從而達到繼承的 效果。下面的代碼是咱們最經常使用的原型繼承方式:

var obj = { name: 'sven' };

var A = function(){}; A.prototype = obj; 

一、首先,嘗試遍歷對象 a 中的全部屬性,但沒有找到 name 這個屬性。
二、查找 name 屬性的這個請求被委託給對象 a 的構造器的原型,它被 a.__proto__ 記錄着而且

指向 A.prototype,而 A.prototype 被設置爲對象 obj。

三、在對象 obj 中找到了 name 屬性,並返回它的值。 

當咱們指望獲得一個「類」繼承自另一個「類」的效果時,每每會用下面的代碼來模擬實現:

var A = function(){}; A.prototype = { name: 'sven' };
var B = function(){}; B.prototype = new A();
varb=newB();
console.log( b.name ); // 輸出:sven

再看這段代碼執行的時候,引擎作了什麼事情。

一、首先,嘗試遍歷對象 b 中的全部屬性,但沒有找到 name 這個屬性。
二、查找 name 屬性的請求被委託給對象 b 的構造器的原型,它被 b.__proto__ 記錄着而且指向 B.prototype,而 B.prototype 被設置爲一個經過 new A()建立出來的對象。
三、在該對象中依然沒有找到 name 屬性,因而請求被繼續委託給這個對象構造器的原型 A.prototype。
四、在 A.prototype 中找到了 name 屬性,並返回它的值。

最後還要留意一點,原型鏈並非無限長的。如今咱們嘗試訪問對象 a address 屬性。而 對象 b 和它構造器的原型上都沒有 address 屬性,那麼這個請求會被最終傳遞到哪裏呢?

實際上,當請求達到 A.prototype,而且在 A.prototype 中也沒有找到 address 屬性的時候, 請求會被傳遞給 A.prototype 的構造器原型 Object.prototype,顯然 Object.prototype 中也沒有 address 屬性,但 Object.prototype 的原型是 null,說明這時候原型鏈的後面已經沒有別的節點了。 因此該次請求就到此打住,a.address 返回 undefined

a.address // 輸出:undefined

相關文章
相關標籤/搜索