面向?qū)ο?/h2>
??面向?qū)ο笫且环N程序設(shè)計(jì)的思想撑教,與面向過(guò)程不同叹话,它引入了類(lèi)的概念偷遗,將性質(zhì)相似的一類(lèi)物體抽象出來(lái),作為設(shè)計(jì)圖一般的存在渣刷,以實(shí)體的方式描述業(yè)務(wù)鹦肿,重心放在了參與事務(wù)的對(duì)象身上,而不是逐步分離的步驟上辅柴。
??面向?qū)ο笥腥齻€(gè)特征:封裝箩溃、繼承、多態(tài)碌嘀,關(guān)于繼承涣旨,可以在讀完本文后,去看看我的另一片文章股冗,新手看JS的六張繼承方式霹陡,這里暫且先不多做解釋。
與面向過(guò)程的區(qū)別
??這里借用一下百度知道上某位仁兄的解釋?zhuān)?br>
??例如五子棋游戲,面向過(guò)程的設(shè)計(jì)思路就是首先分析問(wèn)題的步驟:
??1止状、開(kāi)始游戲烹棉,
??2、黑子先走怯疤,
??3浆洗、繪制畫(huà)面,
??4集峦、判斷輸贏(yíng)伏社,
??5、輪到白子塔淤,
??6摘昌、繪制畫(huà)面,
??7高蜂、判斷輸贏(yíng)聪黎,
??8、返回步驟2备恤,
??9挺举、輸出最后結(jié)果。
??把上面每個(gè)步驟用分別的函數(shù)來(lái)實(shí)現(xiàn)烘跺,問(wèn)題就解決了湘纵。
??而面向?qū)ο?/em>的設(shè)計(jì)則是從另外的思路來(lái)解決問(wèn)題。整個(gè)五子棋可以分為 :
??1滤淳、黑白雙方梧喷,這兩方的行為是一模一樣的,
??2脖咐、棋盤(pán)系統(tǒng)铺敌,負(fù)責(zé)繪制畫(huà)面,
??3屁擅、規(guī)則系統(tǒng)偿凭,負(fù)責(zé)判定諸如犯規(guī)、輸贏(yíng)等派歌。
??第一類(lèi)對(duì)象(玩家對(duì)象)負(fù)責(zé)接受用戶(hù)輸入弯囊,并告知第二類(lèi)對(duì)象(棋盤(pán)對(duì)象)棋子布局的變化痰哨,棋盤(pán)對(duì)象接收到了棋子的i變化就要負(fù)責(zé)在屏幕上面顯示出這種變化,同時(shí)利用第三類(lèi)對(duì)象(規(guī)則系統(tǒng))來(lái)對(duì)棋局進(jìn)行判定匾嘱。
??以上斤斧,兩種模式的區(qū)別,由此可見(jiàn)一斑霎烙。
JS中的對(duì)象
??JS是解釋性的腳本語(yǔ)言撬讽,對(duì)于類(lèi)的概念并沒(méi)有JAVA那般嚴(yán)謹(jǐn)和規(guī)范,且擁有自己的特性和方法悬垃。
??創(chuàng)建對(duì)象的過(guò)程游昼,便是畫(huà)一份設(shè)計(jì)圖,JS一共提供了 7 種創(chuàng)建的方式(來(lái)自高程三)尝蠕,包括:
??1.工廠(chǎng)模式
??2.構(gòu)造函數(shù)模式
??3.原型模式
??4.組合使用構(gòu)造函數(shù)模式和原型模式
??5.動(dòng)態(tài)原型模式
??6.寄生構(gòu)造函數(shù)模式
??7.穩(wěn)妥構(gòu)造函數(shù)模式
??其中使用最廣泛烘豌、認(rèn)同度最高的方式是第四種:組合使用構(gòu)造函數(shù)模式和原型模式,下面對(duì)每種方式進(jìn)行粗略的描述趟佃。
創(chuàng)建對(duì)象
1.工廠(chǎng)模式
function createPerson(name,age){
var o = new Object();
o.name = name;
o.age = age;
o.sayName = function(){
alert(this.name)
};
return o;
}
var person = createPerson("亞當(dāng)",99);
??接收兩個(gè)參數(shù)扇谣,在函數(shù)內(nèi)部創(chuàng)建一個(gè)對(duì)象,然后將參數(shù)綁定后再返回闲昭,可以實(shí)現(xiàn)封裝一個(gè)類(lèi)的功能罐寨,但缺點(diǎn)是所有的對(duì)象的都是Object,無(wú)法準(zhǔn)確判斷它們的類(lèi)型序矩,比如“人”類(lèi)是Object鸯绿,“動(dòng)物”類(lèi)也是Object。
??于是出現(xiàn)了構(gòu)造函數(shù)模式簸淀。
2.構(gòu)造函數(shù)模式
function Person(name,age){ //注意:首字母大寫(xiě)(慣例)
this.name = name;
this.age = age;
this.sayName = function(){
alert(this.name)
};
}
var person = new Person("亞當(dāng)",99);
??不用return對(duì)象瓶蝴,將屬性和方法直接給了this對(duì)象,這樣便可以用alert(person instanceof Person);//ture
來(lái)檢測(cè)對(duì)象的類(lèi)型租幕,這意味著將來(lái)可以將Person標(biāo)識(shí)為一種特定的類(lèi)型舷手,更利于類(lèi)的概念。
??有了“類(lèi)”的模板劲绪,就可以照著模子捏人了男窟,使用構(gòu)造函數(shù)創(chuàng)建對(duì)象,必須使用到new操作符贾富,若是當(dāng)做普通函數(shù)來(lái)使用歉眷,就相當(dāng)是為全局對(duì)象添加了屬性,最后會(huì)出現(xiàn)window.sayName();//打印出傳入的name變量
颤枪,而使用new來(lái)調(diào)用構(gòu)造函數(shù)會(huì)經(jīng)歷一下四個(gè)步驟:
??1.創(chuàng)建一個(gè)新對(duì)象
??2.將構(gòu)造函數(shù)的作用域賦給新對(duì)象
??3.執(zhí)行構(gòu)造函數(shù)中的代碼(為新對(duì)象添加屬性)
??4.返回這個(gè)新對(duì)象
??構(gòu)造函數(shù)模式同樣有其缺陷汗捡,比如上面的例子中,如果創(chuàng)建了兩個(gè)“人”畏纲,就有兩個(gè)同樣的sayName()方法扇住,可以實(shí)現(xiàn)同樣的功能(打印名字)春缕,一個(gè)兩個(gè)還好,如果我們有成百上千個(gè)Person實(shí)例的話(huà)台囱,name就有千百個(gè)satName()方法淡溯,這在內(nèi)存中的開(kāi)銷(xiāo)無(wú)疑是極大的读整,既然是同樣的功能簿训,那么讓它們共同使用一個(gè)函數(shù)就足夠了,因此可以將這個(gè)函數(shù)摘出來(lái)米间,這樣寫(xiě):
function Person(name,age){ //注意:首字母大寫(xiě)(慣例)
this.name = name;
this.age = age;
this.sayName = sayName;
}
function sayName(){
alert(this.name);
}
??將內(nèi)部引用外部命名的函數(shù)强品,而將函數(shù)體放在外面,這樣指向的就是同一個(gè)方法了屈糊,只是如此一來(lái)sayName這個(gè)方法相當(dāng)于是放在了全局作用域中的榛,但方法本身卻只想讓Person的對(duì)象使用,大炮打蚊子逻锐,有點(diǎn)小尷尬夫晌,同時(shí)類(lèi)的封裝性也遭到了破壞,由此問(wèn)題昧诱,便引出了第三種創(chuàng)建方法——原型模式晓淀。
3.原型模式
??每個(gè)構(gòu)造函數(shù)都有一個(gè)prototype屬性,這個(gè)屬性是一個(gè)指針盏档,指向一個(gè)對(duì)象凶掰,而這個(gè)對(duì)象的用途,便是容納同一類(lèi)下所有實(shí)例公有的方法和屬性蜈亩,寫(xiě)法如下懦窘。
function Person(){
}
Person.prototype.name = "亞當(dāng)";
Person.prototype.age = "99";
Person.prototype.sayName= function(){
alert(this.name)
};
var person = new Person();
??或者寫(xiě)的更簡(jiǎn)潔一些:
Person.prototype = {
name : "亞當(dāng)",
age : "99",
sayName : function(){
alert(this.name);
}
}
??好處很明顯,同一類(lèi)下所有對(duì)象可以共享屬性和方法稚配,當(dāng)然畅涂,缺點(diǎn)一樣明顯,創(chuàng)建對(duì)象的時(shí)候無(wú)法傳入自定義參數(shù)道川,除非設(shè)置如person1.name = "夏娃";
才會(huì)覆蓋掉原來(lái)的名字午衰,更為嚴(yán)重的是,如果Person的原型中包含了一個(gè)數(shù)組(引用類(lèi)型)愤惰,如果一個(gè)對(duì)象修改了這個(gè)數(shù)組苇经,其他對(duì)象的數(shù)組都會(huì)發(fā)生變化,因?yàn)橐妙?lèi)型的變量指向的是同一塊內(nèi)存地址宦言,這樣事情就變得很麻煩了扇单。
??構(gòu)造函數(shù)模式無(wú)法設(shè)置共享的屬性,而原型模式無(wú)法自定義屬性奠旺,那如果將兩者優(yōu)點(diǎn)結(jié)合起來(lái)蜘澜,那不是天下無(wú)敵了嗎J┝鳌?
??所以鄙信,我們有了第四種方式——組合使用構(gòu)造函數(shù)模式和原型模式瞪醋。
4.組合使用構(gòu)造函數(shù)模式和原型模式
??不多說(shuō),直接上代碼:
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype = {
constructor : Person, //確保實(shí)例的構(gòu)造函數(shù)指向Person
sayName : function(){
alert(this.name);
}
}
var person = new Person("亞當(dāng)",99);
??可以自定義的屬性(包括引用類(lèi)型)都放在構(gòu)造函數(shù)里装诡,隨便修改都不會(huì)影響其他實(shí)例银受,而公共的方法則放在原型對(duì)象中,避免資源浪費(fèi)鸦采。
??OJBK宾巍,萬(wàn)事大吉!這種模式也是目前在ECMAScript中使用最廣泛渔伯、認(rèn)同度最高的一種創(chuàng)建自定義的方法顶霞。
??至此,基本的幾種已經(jīng)介紹完了锣吼,后面三種會(huì)簡(jiǎn)單介紹一下选浑,不想繼續(xù)深入的小伙伴們可以在這里搬小板凳撤了
5.動(dòng)態(tài)原型模式
??當(dāng)我們?yōu)閷?duì)象定義一個(gè)方法時(shí),有時(shí)可能存在沖突玄叠,必要的情況下古徒,我們可以檢查某個(gè)應(yīng)該存在的方法是否有效,如果有效诸典,看一眼走人描函,如果無(wú)效,我們?cè)俪跏蓟汀?/p>
function Person(name,age){
this.name = name;
this.age = age;
}
//方法
if(typeof this.sayName != "function"){ //如果sayName不是函數(shù)
Person.prototype.sayName= function(){
alert(this.name)
}
};
??如上述代碼狐粱,僅當(dāng)sayName方法不存在的情況下舀寓,才會(huì)在原型中添加此方法,而且只會(huì)在初次調(diào)用構(gòu)造函數(shù)的時(shí)候才會(huì)執(zhí)行這條語(yǔ)句肌蜻,一旦定義后互墓,由于是定義在原型上的方法,所有對(duì)象之后都可以直接調(diào)用了蒋搜。
??這種方法的缺陷篡撵,同樣是不能重寫(xiě)原型,否則會(huì)切斷現(xiàn)有實(shí)例與心源性之間的聯(lián)系豆挽。
6.寄生構(gòu)造函數(shù)模式
??唔...在前面幾種模式都不適用的情況下(應(yīng)該不會(huì)遇到吧...)育谬,可以使用寄生構(gòu)造函數(shù)模式創(chuàng)建對(duì)象,基本思想是:創(chuàng)建一個(gè)函數(shù)帮哈,其作用僅僅只是封裝創(chuàng)建對(duì)象的代碼膛檀,然后再返回新創(chuàng)建的對(duì)象。
function Person(name,age){
var o = new Object();
o.name = name;
o.age = age;
o.sayName = function(){
alert(this.name)
};
return o;
}
var person = new Person("亞當(dāng)",99);
??除了用new操作符以外,其余寫(xiě)法和工廠(chǎng)模式一模一樣咖刃,一般會(huì)在特殊情況下使用它泳炉,例如要?jiǎng)?chuàng)建一個(gè)數(shù)組對(duì)象(Array),但在這個(gè)對(duì)象中要添加新的方法嚎杨,直接修改Array的構(gòu)造函數(shù)的話(huà)花鹅,程序里所有的數(shù)組都變了,GG枫浙,所以可以使用這個(gè)模式刨肃。代碼如下:
function specialArray(){
var arr = new Array();
arr.newFunction = function(){
alert("我叫數(shù)組的新方法")
}
balabalabala... //其他要添加的新方法或操作
return arr;
}
var list = new specialArray();
list.newFunction(); //我叫數(shù)組的新方法
??要注意,返回的對(duì)象與構(gòu)造函數(shù)之間沒(méi)有關(guān)系自脯,不能使用instanceof來(lái)確定對(duì)象類(lèi)型之景,這一點(diǎn)與工廠(chǎng)模式相同斤富,因此建議盡可能不要使用這種方法膏潮。
7.穩(wěn)妥構(gòu)造函數(shù)模式
??穩(wěn)妥對(duì)象,指的是沒(méi)有公共屬性满力,也不引用this對(duì)象焕参,這種模式適合在禁止使用 this 和 new 的環(huán)境中,或者在防止數(shù)據(jù)被其他應(yīng)用程序(如Mashup程序)改動(dòng)時(shí)使用油额,除了不使用 this 和 new 以外叠纷,和寄生構(gòu)造函數(shù)模式類(lèi)似,代碼如下:
function Person(name,age){
var o = new Object();
//可以在這里定義私有變量和屬性
o.sayName = function(){
alert(name)
};
return o;
}
var person = Person("亞當(dāng)",99);
person.sayName(); //亞當(dāng)
??除了使用sayName() 方法外潦嘶,沒(méi)有其他辦法訪(fǎng)問(wèn) name 的值涩嚣,方法中定義的私有變量和屬性也無(wú)法影響傳入的 name 值,安全性杠杠的掂僵!
??當(dāng)然航厚,與寄生構(gòu)造函數(shù)模式、工廠(chǎng)模式相同锰蓬,它也不能使用 instanceof 檢測(cè)其類(lèi)型幔睬。
總結(jié)
??至此,JS面向?qū)ο笈c其中創(chuàng)建方法基本結(jié)束了芹扭,如文章有問(wèn)題麻顶,歡迎指正!2湛ā辅肾!