前言
《JavaScript設(shè)計模式與開發(fā)實踐》是由騰訊的前端團(tuán)隊Alloy Team出品的一本關(guān)于js設(shè)計模式的書籍绪励。全書主要分為3大部分:基礎(chǔ)知識、設(shè)計模式和設(shè)計原則和編程技巧疏魏⊥W觯基礎(chǔ)知識主要是介紹了js語言的面向?qū)ο笏枷搿⒍鄳B(tài)大莫、封裝蛉腌、原型、閉包、高階函數(shù)等烙丛;設(shè)計模式以沒中模式一個章節(jié)介紹了16種經(jīng)常在js開發(fā)中用的設(shè)計模式舅巷;設(shè)計原則和編程技巧,從新的高度講解常用到的開發(fā)和設(shè)計技巧河咽。
本文主要基于全書第一部分: js基礎(chǔ)知識的閱讀經(jīng)驗钠右,進(jìn)行總結(jié)。
動態(tài)語言和鴨子類型
靜態(tài)語法: 一種需要進(jìn)行編譯的語言忘蟹,其在編譯時決定了數(shù)據(jù)的類型飒房,同時在編譯時能夠發(fā)現(xiàn)程序中的錯誤。它能夠讓程序員按照相應(yīng)的約束進(jìn)行開發(fā)媚值,但是會在一定程度上降低開發(fā)效率狠毯。
動態(tài)語言:運行時才能決定數(shù)據(jù)的類型。編碼簡潔褥芒,程序員可以把更多的精力放到邏輯實現(xiàn)上去嚼松。
鴨子模型: 走起路來想鴨子,叫起來像鴨子锰扶,那么它就是鴨子惜颇。 鴨子模型知道我們只關(guān)注對象的行為,而不是關(guān)注對象本身少辣,也就是關(guān)注has-a凌摄,而不是is-a。
鴨子模型可以指導(dǎo)我們在動態(tài)語言中實現(xiàn)一個原則:面向接口編程而不是面向?qū)崿F(xiàn)編程漓帅。如:一個對象如有push和pop方法锨亏,并且這些方法提供了正確的實現(xiàn),它就可以當(dāng)做棧來使用忙干。在靜態(tài)語言中器予,面向接口編程是一件不容易的事情,需要通過抽象類型獎對象向上轉(zhuǎn)型(java里的interface?)捐迫。
多態(tài)
多態(tài)的主旨思想是: 把“做什么”和“誰去做以及怎么做”分離開乾翔,也就是將“不變的事情”和“可能變的事情”分離開。如下面的例子:
var makeSound = function(animal){
if(animal instanceof Duck){
console.log("嘎嘎嘎");
}else if(animal instanceof Chikcen){
console.log("咯咯咯");
}
};
var Duck = function(){};
var Chicken = function(){};
makeSound(new Duck());
makeSound(new Chicken());
上述代碼中沒有把不變的事情(動物會叫)和可能變得事情(不同動物以及不不同動物的叫聲)區(qū)分開來施戴,如果加以區(qū)分反浓,就會成為下面的樣子:
var makeSound = function(animal){
animal.sound();
}
var Duck = function(){};
Duck.prototype.sound = function(){
console.log("嘎嘎嘎");
}
var Chicken = function(){};
Chicken.prototype.sound = function(){
console.log("咯咯咯");
}
makeSound(new Duck());
makeSound(new Chicken());
這樣以后才增加新的動物時就可以只關(guān)注變化的東西,而不要在去處理不要變化的東西了赞哗,如增加狗叫:
var Dog = function(){};
Dog.prototype.sound = funciton(){
console.log("汪汪汪");
}
makeSound(new Dog());
靜態(tài)語言(如java)實現(xiàn)多態(tài)的基本思想是基類定義此方法雷则,然后子類給以不同的實現(xiàn),在進(jìn)行調(diào)用時通過對象的多態(tài)實現(xiàn)方法的調(diào)用肪笋。
總結(jié)起來: 多態(tài)最根本的作用就是通過把過程化的條件分支語句轉(zhuǎn)成對象的多態(tài)性月劈,從而消除這些條件分支語句度迂;它時刻在提醒我們做什么和怎么去做是可一個分開的。
在設(shè)計模式中猜揪,命令模式惭墓、組合模式以及策略模式都是多態(tài)的典型代表,這些可以在后續(xù)的文章中講述而姐。
封裝
封裝可以分為:封裝數(shù)據(jù)诅妹、封裝實現(xiàn)、封裝數(shù)據(jù)和封裝變化毅人。
封裝數(shù)據(jù)主要體現(xiàn)在局部作用域吭狡,對外隱藏,如下面的代碼段:
js:
var myObj = function(){
var name = "ahu";
return {
getName: function(){
return name;
}
}
}
console.log(myObj.name) // undefined
console.log(myObj.getName()) // ahu
java:
public Class myObj{
private name;
public myObj(name){
this.name = name;
}
public getName(){
return name;
}
}
myObj nameInfo = new myObj("ahu");
nameInfo.getName() // ahu
封裝實現(xiàn):對象對它自己的行為負(fù)責(zé)丈莺,其他對象或者用戶不關(guān)心它的內(nèi)部實現(xiàn)划煮。這樣使得對象之間的耦合松散,對象之間通過暴露的API接口進(jìn)行通信缔俄。例如迭代器方法(js中的each方法)弛秋,它負(fù)責(zé)遍歷對象,其內(nèi)部實現(xiàn)改變了俐载,但只要功能不變蟹略,對用戶來說都是不變的。
封裝類型: 封裝類型是通過抽象類和接口來實現(xiàn)的遏佣,如:java的多態(tài)
封裝變化: 找到變化并封裝之是《設(shè)計模式》的出發(fā)點挖炬,23種的設(shè)計模式被劃分為:創(chuàng)建型模式、結(jié)構(gòu)性模式和行為型模式状婶。創(chuàng)建型模式的目的是封裝創(chuàng)建對象的變化意敛;結(jié)構(gòu)型模式封裝的是對象之間的組合關(guān)系;行為型模式封裝的是對象的行為變化膛虫。
原型模式(繼承)
原型模式在設(shè)計模式上來看草姻,是一種創(chuàng)建對象的模式。原型模式很大程序上是基于clone來實現(xiàn)的稍刀,clone是創(chuàng)建對象的一種手段撩独。 如ECMAScript5中提供了Object.create實現(xiàn)對象的clone,不支持此方法的瀏覽器可以用下面的方法進(jìn)行兼容:
Objcet.create = Object.create || function(_obj){
var F = function(){};
F.prototype = _obj;
return new F();
}
js中的原型繼承账月,其遵循以下基本原則:
- 所有的數(shù)據(jù)都是對象(其實js此處實現(xiàn)并不好综膀,基礎(chǔ)類型number、string捶障、boolean僧须、undefined需要進(jìn)行封裝才能得到對象)
- 要得到一個對象,不是通過實例化類项炼,而是找到一個對象作為原型并clone它
- 對象會記住它的原型(proto屬性指向了構(gòu)造函數(shù)的constructor)
- 如果對象無法響應(yīng)某個請求担平,它會把這個請求委托給它自己的原型(原形鏈搜索)
如下程序:
function person(name){ // person不是類 而是一個構(gòu)造器 js中的函數(shù),既可以作為普通函數(shù)調(diào)用锭部,也可以作為構(gòu)造器調(diào)用
this.name = name;
}
Person.prototype.getName = function(){
return this.name;
}
var aa = new Person("ahu") // new時 此時函數(shù)是一個構(gòu)造器
// new操作和一下過程是等同的
var ObjectFactory = function(){
var obj = new Object(), // 從Object.prototype上clone一個空的對象
Constructor = [].shift(call).agruments; // 獲得構(gòu)造器暂论,如上面的Person
obj.__proto__ = ConStructor.prototype; // 指向正確的原型, 這也是在一些框架中經(jīng)常出現(xiàn):AA.prototype.constructor = AA 的修正
var ret = Constructor.apply(obj, arguments); // 確保構(gòu)造器總是返回一個對象
return typeof ret === 'object' ?ret :obj;
}
var bb = ObjectFactory(person, "ahu");
console.log(Object.getPrototypeOf(a) === Person.prototype); // true
套用Peter Norving的一句話:設(shè)計模式是對語言不足的補充, 如果要是有設(shè)計模式拌禾, 不如去找一門更好的語言取胎。