Javascript 繼承

原文鏈接:

JavaScript面向?qū)ο笤斀猓ㄒ唬?/a>
JavaScript面向?qū)ο笤斀猓ǘ?/a>
JavaScript面向?qū)ο笤斀猓ㄈ?/a>
JavaScript面向?qū)ο笤斀猓ㄋ模?/a>
繼承的優(yōu)缺點整理鏈接

概述

  • 本文只做整理知識

什么是原型鏈

1.當(dāng)以讀取模式訪問一個實例屬性時瓷胧,首先會在實例中搜索該屬性旁涤。如果沒有找到該屬性仪媒,則會繼續(xù)搜索實例的原型姜钳。在通過原型鏈實現(xiàn)繼承的情況下,搜索過程就得以沿著原型鏈繼續(xù)向上领迈。
2.在找不到屬性或方法的情況下彻磁,搜索過程總是要一環(huán)一環(huán)地前行到原型鏈末端才會停下來。

繼承的方式有哪些:

  • 1狸捅、原型鏈繼承
  • 2衷蜓、借用構(gòu)造函數(shù)繼承
  • 3、組合繼承(組合原型鏈繼承和借用構(gòu)造函數(shù)繼承)
  • 4尘喝、原型式繼承
  • 5磁浇、寄生式繼承
  • 6、寄生組合式繼承

一朽褪、原型鏈繼承

  • 代碼
// 1.定義Animal的構(gòu)造函數(shù)
function Animal() {
    this.colors = ["red", "green"]
}

// 2.給Animal添加方法
Animal.prototype.animalFunction = function () {
    console.log(this.colors)
}

// 3.定義Person的構(gòu)造函數(shù)
function Person() {
    this.personProperty = "Person"
}

// 4.給Person賦值新的原型對象
Person.prototype = new Animal()

// 5.給Person添加方法
Person.prototype.personFunction = function () {
    console.log(this.personProperty)
}

// 6.創(chuàng)建Person對象, 并且調(diào)用方法
var person1 = new Person()
var person2 = new Person()

console.log(person1.colors) // red,green
console.log(person2.colors) // red,green

person1.colors.push("blue")

console.log(person1.colors) // red,green,blue
console.log(person2.colors) // red,green,blue

// 創(chuàng)建Person的實例
var person = new Person();
person.animalFunction(); // red,green,blue
person.personFunction(); //Person
  • 原型鏈的其他問題:
    1.原型鏈存在最大的問題是關(guān)于引用類型的屬性.
    2.通過上面的原型實現(xiàn)了繼承后, 子類的person對象繼承了(可以訪問)Animal實例中的屬性(animalProperty).
    3.但是如果這個屬性是一個引用類型(比如數(shù)組或者其他引用類型), 就會出現(xiàn)問題.
    4.在創(chuàng)建子類型的實例時置吓,不能向父類型的構(gòu)造函數(shù)中傳遞參數(shù)。
    5.實際上缔赠,應(yīng)該說是沒有辦法在不影響所有對象實例的情況下衍锚,給父類型的構(gòu)造函數(shù)傳遞參數(shù)。
    6.從而可以修改父類型中屬性的值, 在創(chuàng)建構(gòu)造函數(shù)的時候就確定一個值.

  • 原型鏈簡單總結(jié):
    1.通過實現(xiàn)原型鏈嗤堰,本質(zhì)上擴展了本章前面介紹的原型搜索機制构拳。
    2.當(dāng)以讀取模式訪問一個實例屬性時,首先會在實例中搜索該屬性梁棠。如果沒有找到該屬性置森,則會繼續(xù)搜索實例的原型。在通過原型鏈實現(xiàn)繼承的情況下符糊,搜索過程就得以沿著原型鏈繼續(xù)向上凫海。
    3.在找不到屬性或方法的情況下,搜索過程總是要一環(huán)一環(huán)地前行到原型鏈末端才會停下來男娄。

二行贪、借用構(gòu)造函數(shù)繼承(經(jīng)典繼承)

  • 代碼
// 創(chuàng)建Animal的構(gòu)造函數(shù)
function Animal() {
    this.colors = ["red", "green"]
}

// 創(chuàng)建Person的構(gòu)造函數(shù)
function Person() {
    // 繼承Animal的屬性
    Animal.call(this)

    // 給自己的屬性賦值
    this.name = "Coderwhy"
}

// 創(chuàng)建Person對象
var person1 = new Person()
var person2 = new Person()

console.log(person1.colors) // red,greem
console.log(person2.colors) // red,greem
person1.colors.push("blue")
console.log(person1.colors) // red,green,blue
console.log(person2.colors) // red,green
  • 代碼解析:
    1.我們通過在Person構(gòu)造函數(shù)中, 使用call函數(shù), 將this傳遞進去.
    2.這個時候, 當(dāng)Animal中有相關(guān)屬性初始化時, 就會在this對象上進行初始化操作.
    3.這樣就實現(xiàn)了類似于繼承Animal屬性的效果.
  • 經(jīng)典繼承的問題:
    1.對于經(jīng)典繼承理解比較深入, 你已經(jīng)能發(fā)現(xiàn): 經(jīng)典繼承只有屬性的繼承, 無法實現(xiàn)方法的繼承.
    2.因為調(diào)用call函數(shù), 將this傳遞進去, 只能將父構(gòu)造函數(shù)中的屬性初始化到this中.
    3.但是如果函數(shù)存在于父構(gòu)造函數(shù)的原型對象中, this中是不會有對應(yīng)的方法的.
  • 回顧原型鏈和經(jīng)典繼承:
    1.原型鏈存在的問題是引用類型問題和無法傳遞參數(shù), 但是方法可以被繼承
    2.經(jīng)典繼承是引用類型沒有問題, 也可以傳遞參數(shù), 但是方法無法被繼承.
    3.怎么辦呢? 將兩者結(jié)合起來怎么樣?

三、組合繼承

  • 描述:組合繼承(原型鏈和經(jīng)典繼承組合)
  • 組合繼承思想:
    1.組合繼承就是發(fā)揮原型鏈和經(jīng)典繼承各自的優(yōu)點來完成繼承的實現(xiàn).
    2.使用原型鏈實現(xiàn)對原型屬性和方法的繼承.
    3.通過經(jīng)典繼承實現(xiàn)對實例屬性的繼承, 以及可以在構(gòu)造函數(shù)中傳遞參數(shù).
  • 代碼
// 1.創(chuàng)建構(gòu)造函數(shù)的階段
// 1.1.創(chuàng)建Animal的構(gòu)造函數(shù)
function Animal(age) {
    this.age = age
    this.colors = ["red", "green"]
}

// 1.2.給Animal添加方法
Animal.prototype.animalFunction = function () {
    console.log("Hello Animal")
}

// 1.3.創(chuàng)建Person的構(gòu)造函數(shù)
function Person(name, age) {
    Animal.call(this, age)
    this.name = name
}

// 1.4.給Person的原型對象重新賦值
Person.prototype = new Animal(0)

// 1.5.給Person添加方法
Person.prototype.personFunction = function () {
    console.log("Hello Person")
}

// 2.驗證和使用的代碼
// 2.1.創(chuàng)建Person對象
var person1 = new Person("Coderwhy", 18)
var person2 = new Person("Kobe", 30)

// 2.2.驗證屬性
console.log(person1.name + "-" + person1.age) // Coderwhy,18
console.log(person2.name + "-" + person2.age) // Kobe,30

// 2.3.驗證方法的調(diào)用
person1.animalFunction() // Hello Animal
person1.personFunction() // Hello Person

// 2.4.驗證引用屬性的問題
person1.colors.push("blue")
console.log(person1.colors) // red,green,blue
console.log(person2.colors) // red,green
  • 組合繼承存在什么問題呢?
    1.組合繼承最大的問題就是無論在什么情況下, 都會調(diào)用兩次父類構(gòu)造函數(shù).
    2.一次在創(chuàng)建子類原型的時候
    3.另一次在子類構(gòu)造函數(shù)內(nèi)部(也就是每次創(chuàng)建子類實例的時候).
    4.所有的子類實例事實上會擁有兩份父類的屬性
    5.一份在當(dāng)前的實例自己里面(也就是person本身的), 另一份在子類對應(yīng)的原型對象中(也就是person.proto里面)
    6.當(dāng)然, 這兩份屬性我們無需擔(dān)心訪問出現(xiàn)問題, 因為默認一定是訪問實例本身這一部分的.

四模闲、原型式繼承

  • 代碼
// 使用原生式繼承
var person = {
    name: "Coderwhy",
    colors: ["red", "green"]
}

// 封裝object()函數(shù)
function object(o) {
    function F() {}
    F.prototype = o
    return new F()
}

// 通過person去創(chuàng)建另外一個對象  Object.create(person) 相當(dāng)于 object(person)
var person1 = Object.create(person)
person1.name = "Kobe"
person1.colors.push("blue")

console.log(person1.name) // Kobe
console.log(person1.colors) // red,green,blue

console.log(person.name) // Coderwhy
console.log(person.colors) // red,green,blue
  • 代碼解析:
    1.這種方式和我們傳統(tǒng)意義上理解的繼承有些不同. 它做的事情是通過一個對象去創(chuàng)建另外一個對象.(利用person去創(chuàng)建person1)
    2.當(dāng)然, person1中繼承過來的屬性是放在了自己的原型對象中的.
    3.也可以給person1自己再次添加name屬性, 這個時候name才是在實例本身中.
  • 原型式繼承的問題:
    1.如果我們只是希望一個對象和另一個對象保持類似的情況下, 原型式繼承完全可以勝任, 這是它的優(yōu)勢.
    2.但是, 原型式繼承依然存在屬性共享的問題, 就像使用原型鏈一樣.

五建瘫、寄生式繼承

  • 思想:寄生式繼承的思路是結(jié)合原型類繼承和工廠模式的一種方式
  • 代碼
// 封裝object函數(shù)
function object(o) {
    function F() {}
    F.prototype = o
    return new F()
}

// 封裝創(chuàng)建新對象的函數(shù)
function createAnother(original) {
    var clone = object(original)
    clone.sayHello = function () {
        console.log("Hello JavaScript")
    }
    return clone
}

// person對象
var person = {
    name: "Coderwhy",
    colors: ["red", "green"]
}

// 新的對象
var person1 = createAnother(person)
person1.sayHello()
  • 代碼解讀:
    1.我們基于person對象, 創(chuàng)建了另外一個對象person1.
    2.在最新的person1對象中, 不僅會擁有person的屬性和方法, 而且還有自己定義的方法
  • 寄生式繼承存在的問題:
    1.寄生式繼承和原型式繼承存在一樣的問題, 引用類型會共享. (因為是在原型式繼承基礎(chǔ)上的一種封裝)
    2.另外寄生式繼承還存在函數(shù)無法復(fù)用的問題, 因為每次createAnother一個新的對象, 都需要重新定義新的函數(shù).

六、寄生組合式繼承

  • 代碼
// 定義object函數(shù)
function object(o) {
    function F() {}
    F.prototype = o
    return new F()
}

// 定義寄生式核心函數(shù)
function inhreitPrototype(subType, superType) {
    var prototype = object(superType.prototype)
    prototype.constructor = subType
    subType.prototype = prototype
}

// 定義Animal構(gòu)造函數(shù)
function Animal(age) {
    this.age = age
    this.colors = ["red", "green"]
}

// 給Animal添加方法
Animal.prototype.animalFunction = function () {
    console.log("Hello Animal")
}

// 定義Person構(gòu)造函數(shù)
function Person(name, age) {
    Animal.call(this, age)
    this.name = name
}

// 使用寄生組合式核心函數(shù)
inhreitPrototype(Person, Animal)

// 給Person添加方法
Person.prototype.personFunction = function () {
    console.log("Hello Person")
}
  • 優(yōu)點:
    1.這種方式的高效體現(xiàn)在現(xiàn)在它只調(diào)用了一次Animal的構(gòu)造函數(shù).
    2.并且也避免了在原型上面多出的多余屬性, 而且原型之間不會產(chǎn)生任何的干擾(子類型原型和父類型原型之間).
    3.在ES5中, 普遍認為寄生組合式繼承是最理想的繼承范式.

全部繼承的總結(jié)

一尸折、原型鏈繼承

  • 重點:
    • 1啰脚、讓子實例的原型等于父類的實例。
  • 優(yōu)點:
    • 1实夹、實例可繼承的屬性有:實例的屬性和方法橄浓,父類實例的屬性和方法粒梦,父類原型的屬性和方法。
  • 缺點:
    • 1荸实、子實例無法向父類構(gòu)造函數(shù)傳參匀们。
    • 2、繼承單一准给。
    • 3泄朴、所有新實例都會共享父類實例的屬性。(原型上的屬性是共享的露氮,一個實例修改了原型屬性祖灰,另一個實例的原型屬性也會被修改!)

二沦辙、借用構(gòu)造函數(shù)繼承

  • 重點:
    • 用.call()和.apply()將父類構(gòu)造函數(shù)引入子類構(gòu)造函數(shù)(在子類函數(shù)中做了父類函數(shù)的自執(zhí)行(復(fù)制))
  • 優(yōu)點:
    • 1、只繼承了父類構(gòu)造函數(shù)的屬性和方法讹剔,沒有繼承父類原型的屬性和方法油讯。
    • 2、在子實例中可向父實例傳參延欠。
    • 3陌兑、解決了原型鏈繼承缺點1、2由捎、3兔综。
    • 4、可以繼承多個構(gòu)造函數(shù)屬性(call多個)狞玛。
  • 缺點:
    • 1软驰、只能繼承父類構(gòu)造函數(shù)的屬性。

三心肪、組合繼承

  • 重點:
    • 結(jié)合了兩種模式的優(yōu)點(原型鏈繼承和借用構(gòu)造函數(shù)繼承)锭亏,傳參和復(fù)用
  • 優(yōu)點:
    • 1、可以繼承父類原型上的屬性硬鞍,可以傳參慧瘤,可復(fù)用。
    • 2固该、每個新實例引入的構(gòu)造函數(shù)屬性是私有的锅减。
  • 缺點:
    • 1、會調(diào)用兩次父類構(gòu)造函數(shù)伐坏,一次在創(chuàng)建子類原型的時候怔匣,另一次在子類構(gòu)造函數(shù)內(nèi)部(也就是每次創(chuàng)建子類實例的時候).
    • 2鼎文、所有的子類實例事實上會擁有兩份父類的屬性
      一份在當(dāng)前的實例自己里面(也就是person本身的), 另一份在子類對應(yīng)的原型對象中(也就是person.proto里面)

四底循、原型式繼承

  • 重點:
    • 1、用一個函數(shù)包裝一個對象,然后返回這個函數(shù)的調(diào)用簸州,這個函數(shù)就變成了個可以隨意增添屬性的實例或?qū)ο蟆bject.create()就是這個原理亥至。
  • 優(yōu)點:
    • 1信不、類似于復(fù)制一個對象,用函數(shù)來包裝懦砂。
  • 缺點:
    • 1蜒犯、所有實例都會繼承原型上的屬性。
    • 2荞膘、原型式繼承依然存在屬性共享的問題, 就像使用原型鏈一樣.

五罚随、寄生式繼承

  • 重點:
    • 1、寄生式繼承的思路是結(jié)合原型類繼承和工廠模式羽资,封裝繼承過程的函數(shù), 該函數(shù)在內(nèi)部以某種方式來增強對象, 最后再將這個對象返回.
  • 優(yōu)點:
    • 1淘菩、沒有創(chuàng)建自定義類型,因為只是套了個殼子返回對象(這個)屠升,這個函數(shù)順理成章就成了創(chuàng)建的新對象潮改。
  • 缺點:
    • 1、寄生式繼承和原型式繼承存在一樣的問題, 引用類型會共享. (因為是在原型式繼承基礎(chǔ)上的一種封裝)
    • 2腹暖、寄生式繼承還存在函數(shù)無法復(fù)用的問題, 因為每次createAnother一個新的對象, 都需要重新定義新的函數(shù).

六汇在、寄生組合式繼承(完美)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市脏答,隨后出現(xiàn)的幾起案子糕殉,更是在濱河造成了極大的恐慌,老刑警劉巖殖告,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件阿蝶,死亡現(xiàn)場離奇詭異,居然都是意外死亡黄绩,警方通過查閱死者的電腦和手機赡磅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宝与,“玉大人焚廊,你說我怎么就攤上這事∠敖伲” “怎么了咆瘟?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長诽里。 經(jīng)常有香客問我袒餐,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任灸眼,我火速辦了婚禮卧檐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘焰宣。我一直安慰自己霉囚,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布匕积。 她就那樣靜靜地躺著盈罐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪闪唆。 梳的紋絲不亂的頭發(fā)上盅粪,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天,我揣著相機與錄音悄蕾,去河邊找鬼票顾。 笑死,一個胖子當(dāng)著我的面吹牛帆调,可吹牛的內(nèi)容都是我干的奠骄。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼贷帮,長吁一口氣:“原來是場噩夢啊……” “哼戚揭!你這毒婦竟也來了诱告?” 一聲冷哼從身側(cè)響起撵枢,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎精居,沒想到半個月后锄禽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡靴姿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年沃但,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片佛吓。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡宵晚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出维雇,到底是詐尸還是另有隱情淤刃,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布吱型,位于F島的核電站逸贾,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜铝侵,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一灼伤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧咪鲜,春花似錦狐赡、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至隆敢,卻和暖如春发皿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拂蝎。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工穴墅, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人温自。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓玄货,卻偏偏與公主長得像,于是被迫代替她去往敵國和親悼泌。 傳聞我的和親對象是個殘疾皇子松捉,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,927評論 2 355

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

  • 本文先對es6發(fā)布之前javascript各種繼承實現(xiàn)方式進行深入的分析比較,然后再介紹es6中對類繼承的支持以及...
    lazydu閱讀 16,680評論 7 44
  • javascript繼承 ES6之前馆里,JavaScript本質(zhì)上不能算是一門面向?qū)ο蟮恼Z言隘世,因為它對于封裝,繼承鸠踪,...
    程序員不存在的閱讀 622評論 1 0
  • JavaScript 繼承機制的設(shè)計思想就是丙者,原型對象的所有屬性和方法,都能被實例對象共享营密。所以我們只要改變對象的...
    Kevin丶CK閱讀 280評論 0 1
  • 前言 ?在面向?qū)ο缶幊讨行得剑瑢ο蟮睦^承是很重要的一個概念。A 對象通過繼承 B 對象评汰,就能直接擁有 B 對象的所有屬...
    agamgn閱讀 126評論 0 1
  • 本篇文章會分別從ES5和ES6的角度上來學(xué)習(xí)JS的繼承纷捞。 由于js不像java那樣是真正面向?qū)ο蟮恼Z言,js是基于...
    充滿正能量的灰灰閱讀 449評論 0 2