JavaScript高級程序設(shè)計——面向?qū)ο?/h1>

面向?qū)ο蟮恼Z言都有一個標(biāo)志答姥,那就是它們都有類的概念,而通過類可以創(chuàng)建任意多個具有相同屬性和方法的對象铣缠。但是ECMAScript中沒有類的概念,因此它的對象也與基于類的語言中的對象有所不同。

創(chuàng)建自定義對象的兩種方法:

// 創(chuàng)建一個Object實例
var person = new Object();
person.name = 'andy';
person.age = 29;

person.sayName = function () {
    alert(this.name)
}

// 對象字面量
var person = {
    name: 'andy',
    age: 29,

    sayName: function () {
        console.log(this.name)
    }
}

1. 工廠模式

這種模式抽象了創(chuàng)建具體對象的過程卿拴∏贸ぃ考慮到在ECMAScript中無法創(chuàng)建類,開發(fā)人員就發(fā)明了一種函數(shù)捡硅,用函數(shù)來封裝以特定接口創(chuàng)建對象的細(xì)節(jié)哮内。

function createPerson(name, age) {
    var o = new Object();
    o.name = name;
    o.age = age;
    o.sayName = function () {
        alert(o.name);
    }
    return o;
}
var person1 = createPerson('andy', 21);

函數(shù)createPerson()能夠根據(jù)接受的參數(shù)來構(gòu)建一個包含所有必要信息的person對象∽尘拢可以無數(shù)次地調(diào)用這個函數(shù)北发,而每次它會返回一個包含兩個屬性一個方法的對象。工程模式雖然解決了創(chuàng)建多個類似對象的問題喷屋,但卻沒有解決對象識別的問題(即怎樣知道一個對象的類型)琳拨。隨著JavaScript的發(fā)展,又一個新模式出現(xiàn)了屯曹。

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

ECMAScript中的構(gòu)造函數(shù)可以用來創(chuàng)建特定類型的對象狱庇。像Object和Array這樣的原生構(gòu)造函數(shù),在運行時會自動出現(xiàn)在執(zhí)行環(huán)境中恶耽。此外密任,也可以創(chuàng)建自定義的構(gòu)造函數(shù),從而定義自定義對象類型的屬性和方法驳棱。

function Person (name, age) {
    this.name = name;
    this.age = age;
    this.sayName = function () {
        alert(this.name);
    }
}
var per = new Person('andy', 21);
per.sayName();    // andy

在該構(gòu)造函數(shù)中批什,有以下幾點不同:

  • 沒有顯式地創(chuàng)建對象;
  • 直接將屬性和方法賦給了this對象社搅;
  • 沒有return語句驻债。

要創(chuàng)建Person對象的新實例乳规,必須使用new操作符。以這種方式調(diào)用構(gòu)造函數(shù)實際上會經(jīng)歷以下4個步驟:

  • 創(chuàng)建一個新對象 合呐;
  • 將構(gòu)造函數(shù)的作用域賦給新對象暮的;
  • 執(zhí)行構(gòu)造函數(shù)中的代碼;
  • 返回新對象淌实。

我們可以用instanceof操作符來進(jìn)行對象檢測:

console.log(per.constructor === Person);    // true
console.log(per instanceof Object);    // true
console.log(per instanceof Person);    // true
console.log(per instanceof String);    // false

構(gòu)造函數(shù)模式雖然好用冻辩,但也并非沒有缺點。使用構(gòu)造函數(shù)的主要問題拆祈,就是每個方法都要在每個實例上重新創(chuàng)建一遍恨闪。每定義一個函數(shù),也就實例化了一個對象放坏。以這種方式創(chuàng)建函數(shù)咙咽,會導(dǎo)致不同的作用域鏈和標(biāo)識符解析,但創(chuàng)建Function新實例的機制仍然是相同的淤年。因此钧敞,不同實例上的同名函數(shù)是不相等的。

console.log(per1.sayName === per2.sayName);    // false

創(chuàng)建兩個完全同樣任務(wù)的Function實例的確沒有必要麸粮;況且有this對象在溉苛,根本不用再執(zhí)行代碼前就把函數(shù)綁定到特定對象上面。所以這就促使我們可以使用原型模式來自定義對象弄诲。

3. 原型模式

a. 原型語法

我們創(chuàng)建的每一個函數(shù)都有一個prototype屬性愚战,這個屬性是一個指針,指向一個對象威根。而這個對象的用途是包含由特定類型的所有實例共享的屬性和方法凤巨。使用原型對象的好處是可以讓所有對象實例共享它所包含的屬性和方法。換句話說洛搀,不必在構(gòu)造函數(shù)中定義對象實例的信息敢茁,而是可以將這些信息直接添加到原型對象中:

function Person() {
}
Person.prototype.name = 'andy';
Person.prototype.age = 13;
Person.prototype.sayName = function () {
    console.log(this.name);
}


var per1 = new Person();
var per2 = new Person();
per1.sayName();    // andy
per2.sayName();    // andy

per1.name = 'qiqi';
per1.sayName();    // qiqi

在此,我們將sayName()方法的所有屬性直接添加到了Person的prototype屬性中留美,構(gòu)造函數(shù)編程了空函數(shù)彰檬。即使如此,也仍然可以通過調(diào)用構(gòu)造函數(shù)來創(chuàng)建新對象谎砾,而且新對象還會具有相同的屬性和方法逢倍。但與構(gòu)造函數(shù)模式不同的是,新對象的這些屬性和方法是由所有實例共享的景图。也就是說per1和per2訪問的都是同一組屬性和同一個sayName()函數(shù)较雕。

無論什么時候,只要創(chuàng)建了一個函數(shù),就會根據(jù)一組特定的規(guī)則為該函數(shù)創(chuàng)建一個prototype屬性亮蒋,這個屬性指向函數(shù)的原型對象扣典。在默認(rèn)情況下,所有原型對象都會自動獲得一個constructor屬性慎玖,這個屬性包含一個指向prototype屬性所在函數(shù)的指針贮尖。以前邊例子來說,Person.prototype.constructoy指向Person趁怔。通過這個構(gòu)造函數(shù)湿硝,我們還可繼續(xù)為原型對象添加其他屬性和方法。

幾種操作符:

  • isPrototypeOf() 確定對象之間是否存在關(guān)系
console.log(Person.prototype.isPrototypeOf(person1));    // true
  • getPrototypeof() 返回prototype的值
console.log(Object.getPrototypeOf(person1) === Person.prototype);    // true
console.log(Object.getPrototypeOf(person1.name));    // andy
  • hasOwnProperty() 用來檢測一個屬性是存在于實例中润努,還是存在于原型中关斜。
console.log(per1.hasOwnProperty('name'));    // false

per1.name = 'qiqi';
console.log(per1.hasOwnProperty('name'));    // true
  • in 只要通過對象能夠訪問到屬性就返回true,不管是在實例中還是在原型中
console.log('name' in per1);    // true
console.log('name' in Person);    // true
b. 更簡單的原型語法

在前面的例子中铺浇,每添加一個屬性和方法就要敲一遍prototype蚤吹,為了減少不必要的輸入可以采用以下的寫法:

function Person() {
}
Person.prototype = {
    constructor: Person,    // 不添加的話constructor會變化,此處為了使該指針指向原型
    name: 'andy',
    age: 21,
    sayName: function() {
        console.log(this.name);
    }
}

原型模式并不是沒有缺點随抠。首先,它省略了為構(gòu)造函數(shù)傳遞初始化參數(shù)這一環(huán)節(jié)繁涂,結(jié)果所有實例在默認(rèn)情況下都取得相同的屬性值拱她。雖然這會在某種程度上帶來一些不方便,但這不是原型的最大問題扔罪。原型模式的最大問題是由其共享的本性導(dǎo)致的秉沼。

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

結(jié)合以上各種優(yōu)點缺點,在自定義對象的時候可以采用組合使用的方式!

function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype = {
    constructor: Person,
    sayName: function() {
        console.log(this..name);
    }
}

4. 繼承

繼承是面向?qū)ο笳Z言中一個最為津津樂道的概念矿酵,許多面向?qū)ο笳Z言都支持兩種繼承方式:接口繼承和實現(xiàn)繼承唬复。接口繼承只繼承方法簽名,而實現(xiàn)繼承則繼承實際的方法全肮。因為函數(shù)沒有簽名敞咧,所以ECMAScript中無法實現(xiàn)接口繼承,只支持實現(xiàn)繼承辜腺,而且其實實現(xiàn)繼承主要是依靠原型鏈來實現(xiàn)休建。

a. 原型鏈
function SuperType() {
    this.property = true;
}
SuperType.prototype.getSuperValue = function() {
    return this.property;
}
function SubType() {
    this.subproperty = false;
}
SubType.prototype = new SuperType();    // 原型鏈繼承
SubType.prototype.getSubValue = function() {
    return this.subproperty;
}
var ins = new SubType();
console.log(ins.getSubValue());

以上代碼定義了兩個類型:Supertype和SubType。每個類型分別有一個屬性和一個方法评疗。他們的主要區(qū)別是SubType繼承了SuperType测砂,而繼承是通過創(chuàng)建SuperType的實例,并將該實例賦給SubType.prototype實現(xiàn)的百匆。實現(xiàn)的本質(zhì)是重寫原型對象砌些,代之以一個新類型的實例。換句話說加匈,原來存在于SuperType的實例中的所有屬性和方法存璃,現(xiàn)在也存在于SubType中了 仑荐。確立了繼承關(guān)系之后,給SubType.prototype添加了一個方法有巧。

b. 借用構(gòu)造函數(shù)

在解決原型鏈中包含引用類型值所帶來的問題中释漆,開發(fā)人員開始使用一種叫做借用構(gòu)造函數(shù)的技術(shù)。這種技術(shù)的基本思想相當(dāng)簡單篮迎,即在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型構(gòu)造函數(shù)男图。別忘了,函數(shù)只不過是在特定環(huán)境中執(zhí)行代碼的對象甜橱,因此通過使用apply()和call()方法也可以在新創(chuàng)建的對象上執(zhí)行構(gòu)造函數(shù):

function SuperType(name) {
    this.name = name;  
    this.colors = ['red', 'blue', 'black'];
}
function SubType() {
    // 繼承了SuperType逊笆,還傳遞了參數(shù)
    SuperType.call(this, 'andy');
}
c. 組合繼承

組合繼承,有時候也叫偽經(jīng)典繼承岂傲,指的是將原型鏈和借用構(gòu)造函數(shù)的技術(shù)組合到一起难裆,從而發(fā)揮二者之長的一種繼承方式。其背后的思路是使用原型鏈實現(xiàn)對原型屬性和方法的繼承镊掖,而通過借用構(gòu)造函數(shù)來實現(xiàn)對實例屬性的繼承乃戈。這樣,既通過在原型上定義方法實現(xiàn)了函數(shù)的復(fù)用亩进,又能保證每個實例都有它自己的屬性:

function SuperType(name) {
    this.name = name;
    this.colors = ['red',  'blue', 'black'];
}
SuperType.prototype.sayName = function() {
    console.log(this.name);
}
function SubType() {
    // 繼承屬性
    SuperType.call(this, name);
    this.age = age;
}
// 繼承方法
SubType.prototype = new SuperType();
SubType.prototype.sayName = function () {
    console.log(this.name);
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者

  • 序言:七十年代末症虑,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子归薛,更是在濱河造成了極大的恐慌谍憔,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件主籍,死亡現(xiàn)場離奇詭異习贫,居然都是意外死亡,警方通過查閱死者的電腦和手機千元,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進(jìn)店門苫昌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人诅炉,你說我怎么就攤上這事蜡歹。” “怎么了涕烧?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵月而,是天一觀的道長。 經(jīng)常有香客問我议纯,道長父款,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮憨攒,結(jié)果婚禮上世杀,老公的妹妹穿的比我還像新娘。我一直安慰自己肝集,他們只是感情好瞻坝,可當(dāng)我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著杏瞻,像睡著了一般所刀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上捞挥,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天浮创,我揣著相機與錄音,去河邊找鬼砌函。 笑死斩披,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的讹俊。 我是一名探鬼主播垦沉,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼仍劈!你這毒婦竟也來了乡话?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤耳奕,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后诬像,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體屋群,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年坏挠,在試婚紗的時候發(fā)現(xiàn)自己被綠了芍躏。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡降狠,死狀恐怖对竣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情榜配,我是刑警寧澤否纬,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站蛋褥,受9級特大地震影響临燃,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一膜廊、第九天 我趴在偏房一處隱蔽的房頂上張望乏沸。 院中可真熱鬧,春花似錦爪瓜、人聲如沸蹬跃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蝶缀。三九已至,卻和暖如春算灸,著一層夾襖步出監(jiān)牢的瞬間扼劈,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工菲驴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留荐吵,地道東北人。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓赊瞬,卻偏偏與公主長得像先煎,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子巧涧,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,843評論 2 354

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