新手看JS中的面向?qū)ο?/h1>

面向?qū)ο?/h2>

??面向?qū)ο笫且环N程序設(shè)計(jì)的思想,與面向過程不同,它引入了類的概念,將性質(zhì)相似的一類物體抽象出來(lái),作為設(shè)計(jì)圖一般的存在,以實(shí)體的方式描述業(yè)務(wù),重心放在了參與事務(wù)的對(duì)象身上,而不是逐步分離的步驟上。
??面向?qū)ο笥腥齻€(gè)特征:封裝、繼承、多態(tài),關(guān)于繼承,可以在讀完本文后,去看看我的另一片文章,新手看JS的六張繼承方式,這里暫且先不多做解釋。

與面向過程的區(qū)別

??這里借用一下百度知道上某位仁兄的解釋:
??例如五子棋游戲,面向過程的設(shè)計(jì)思路就是首先分析問題的步驟:
??1、開始游戲,
??2、黑子先走,
??3、繪制畫面,
??4、判斷輸贏,
??5、輪到白子,
??6、繪制畫面,
??7、判斷輸贏,
??8、返回步驟2,
??9、輸出最后結(jié)果。

??把上面每個(gè)步驟用分別的函數(shù)來(lái)實(shí)現(xiàn),問題就解決了。

??而面向?qū)ο?/em>的設(shè)計(jì)則是從另外的思路來(lái)解決問題。整個(gè)五子棋可以分為 :
??1、黑白雙方,這兩方的行為是一模一樣的,
??2、棋盤系統(tǒng),負(fù)責(zé)繪制畫面,
??3、規(guī)則系統(tǒng),負(fù)責(zé)判定諸如犯規(guī)、輸贏等。

??第一類對(duì)象(玩家對(duì)象)負(fù)責(zé)接受用戶輸入,并告知第二類對(duì)象(棋盤對(duì)象)棋子布局的變化,棋盤對(duì)象接收到了棋子的i變化就要負(fù)責(zé)在屏幕上面顯示出這種變化,同時(shí)利用第三類對(duì)象(規(guī)則系統(tǒng))來(lái)對(duì)棋局進(jìn)行判定。
??以上,兩種模式的區(qū)別,由此可見一斑。

JS中的對(duì)象

??JS是解釋性的腳本語(yǔ)言,對(duì)于類的概念并沒有JAVA那般嚴(yán)謹(jǐn)和規(guī)范,且擁有自己的特性和方法。
??創(chuàng)建對(duì)象的過程,便是畫一份設(shè)計(jì)圖,JS一共提供了 7 種創(chuàng)建的方式(來(lái)自高程三),包括:
??1.工廠模式
??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.工廠模式

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è)類的功能,但缺點(diǎn)是所有的對(duì)象的都是Object,無(wú)法準(zhǔn)確判斷它們的類型,比如“人”類是Object,“動(dòng)物”類也是Object。
??于是出現(xiàn)了構(gòu)造函數(shù)模式。

2.構(gòu)造函數(shù)模式

function Person(name,age){    //注意:首字母大寫(慣例)
    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)可以將Person標(biāo)識(shí)為一種特定的類型,更利于類的概念。
??有了“類”的模板,就可以照著模子捏人了,使用構(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í)例的話,name就有千百個(gè)satName()方法,這在內(nèi)存中的開銷無(wú)疑是極大的,既然是同樣的功能,那么讓它們共同使用一個(gè)函數(shù)就足夠了,因此可以將這個(gè)函數(shù)摘出來(lái),這樣寫:

function Person(name,age){    //注意:首字母大寫(慣例)
    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í)類的封裝性也遭到了破壞,由此問題,便引出了第三種創(chuàng)建方法——原型模式。

3.原型模式

??每個(gè)構(gòu)造函數(shù)都有一個(gè)prototype屬性,這個(gè)屬性是一個(gè)指針,指向一個(gè)對(duì)象,而這個(gè)對(duì)象的用途,便是容納同一類下所有實(shí)例公有的方法和屬性,寫法如下。

function Person(){
}
Person.prototype.name = "亞當(dāng)";
Person.prototype.age = "99";
Person.prototype.sayName= function(){
    alert(this.name)
};
var person = new Person();

??或者寫的更簡(jiǎn)潔一些:

Person.prototype = {
    name : "亞當(dāng)",
    age : "99",
    sayName : function(){
        alert(this.name);
    }
}

??好處很明顯,同一類下所有對(duì)象可以共享屬性和方法,當(dāng)然,缺點(diǎn)一樣明顯,創(chuàng)建對(duì)象的時(shí)候無(wú)法傳入自定義參數(shù),除非設(shè)置如person1.name = "夏娃";才會(huì)覆蓋掉原來(lái)的名字,更為嚴(yán)重的是,如果Person的原型中包含了一個(gè)數(shù)組(引用類型),如果一個(gè)對(duì)象修改了這個(gè)數(shù)組,其他對(duì)象的數(shù)組都會(huì)發(fā)生變化,因?yàn)橐妙愋偷淖兞恐赶虻氖峭粔K內(nèi)存地址,這樣事情就變得很麻煩了。
??構(gòu)造函數(shù)模式無(wú)法設(shè)置共享的屬性,而原型模式無(wú)法自定義屬性,那如果將兩者優(yōu)點(diǎn)結(jié)合起來(lái),那不是天下無(wú)敵了嗎!?
??所以,我們有了第四種方式——組合使用構(gòu)造函數(shù)模式和原型模式。

4.組合使用構(gòu)造函數(shù)模式和原型模式

??不多說,直接上代碼:

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);

??可以自定義的屬性(包括引用類型)都放在構(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)用了。
??這種方法的缺陷,同樣是不能重寫原型,否則會(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操作符以外,其余寫法和工廠模式一模一樣,一般會(huì)在特殊情況下使用它,例如要?jiǎng)?chuàng)建一個(gè)數(shù)組對(duì)象(Array),但在這個(gè)對(duì)象中要添加新的方法,直接修改Array的構(gòu)造函數(shù)的話,程序里所有的數(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ù)之間沒有關(guān)系,不能使用instanceof來(lái)確定對(duì)象類型,這一點(diǎn)與工廠模式相同,因此建議盡可能不要使用這種方法。

7.穩(wěn)妥構(gòu)造函數(shù)模式

??穩(wěn)妥對(duì)象,指的是沒有公共屬性,也不引用this對(duì)象,這種模式適合在禁止使用 this 和 new 的環(huán)境中,或者在防止數(shù)據(jù)被其他應(yīng)用程序(如Mashup程序)改動(dòng)時(shí)使用,除了不使用 this 和 new 以外,和寄生構(gòu)造函數(shù)模式類似,代碼如下:

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() 方法外,沒有其他辦法訪問 name 的值,方法中定義的私有變量和屬性也無(wú)法影響傳入的 name 值,安全性杠杠的!
??當(dāng)然,與寄生構(gòu)造函數(shù)模式、工廠模式相同,它也不能使用 instanceof 檢測(cè)其類型。

總結(jié)

??至此,JS面向?qū)ο笈c其中創(chuàng)建方法基本結(jié)束了,如文章有問題,歡迎指正!!!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容