原文鏈接:
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ù).