javascript中的繼承
技術標籤:jsjavascript繼承
javascript中的繼承
本章節摘自《javascript設計模式》人民郵電出版社中的繼承一節,感覺收穫很大,便自己作筆記整理一下,方便更好的理解
首先,繼承是什麼
繼承? 什麼是繼承呢,顧名思義,比如父母會把自己的一些特點遺傳給孩子,孩子具有了父母的一些特點,但又不完全一樣,總會有自己的特點, 但又不完全一樣,總會有自己的特點,所以父母和孩子都是獨立的個體,繼承可以使得子類具有父類的屬性和方法。 但是javascript中並不存在現有的機制,怎麼使用javascript實現繼承呢
javascript實現繼承該如何實現呢
- 第一種: 子類的原型物件–類式繼承
宣告父類, 當我們例項化一個父類的時候,新建立的物件複製了父類的建構函式內的屬性和方法,並將_proto_指向了父類的原型物件, 這樣既擁有了父類自己的屬性和方法,也擁有了父類原型的屬性與方法
(function () {
// subclass prototype --- > superClass 例項 ---> 訪問superClass 屬性與方法
// subclass prototype._proto_ = superClass.prototype
function SuperClass(id) {
this.books = ['javascript', 'html', 'css', 'vue', 'react']
this.SuperValue = true
this.id = id
}
// 新增共有方法
SuperClass.prototype.getSuperValue = function () {
return this.SuperValue
}
function subClass(id) {
this.SubValue = false
this.subid = id
}
// 繼承父類, 將子類原型繼承於創建出來的父類的例項
subClass.prototype = new SuperClass(this.subid)
subClass.prototype.getSubValue = function () {
return this.SubValue
}
var instance = new subClass(1)
var instance1 = new subClass(2)
console.log(instance.SuperValue)
console.log(instance.SubValue)
// 缺點: instanceOf只可以檢測當前例項是否在某個類的原型鏈上, 無法判斷繼承,繼承並非例項, 這一點不能弄混
console.log(instance instanceof subClass) // true
console.log(instance instanceof SuperClass) // true
// 通過superclass 的例項 賦值給subclass.prototype
console.log(subClass.prototype instanceof SuperClass) // true
console.log(instance instanceof Object) // 建立的所有物件都是Object的例項 true
console.log(subClass instanceof SuperClass) // false
console.log(instance1.books) // ["javascript", "html", "css", "vue", "react"]
console.log(instance)
instance.books.push('設計模式')
console.log(instance1.books) // ["javascript", "html", "css", "vue", "react", "設計模式"]
類式繼承的缺點
1: 由於子類通過其原型prototype對父類進行的例項化,繼承了父類,如果父類中的共有屬性為引用型別,如果子類修改了父類的屬性, 那麼,就會直接影響到其他子類
2: 由於子類實現的繼承是靠其原型prototype 對父類進行例項化實現的,因此在建立父類的時候,是無法向父類傳遞引數的,因而在建立父類的時候,也無法對父類建構函式的屬性進行初始化
- 建構函式繼承
(function () {
// superClass this指向 superClass
function superClass(id) {
this.books = ['javascript', 'html', 'css', 'vue', 'react'],
this.id = id
}
superClass.prototype.showBooks = function () {
console.log(this.books)
}
// subClass this指向 superClass
function subClass(id) {
// call方法可以改變函式的作用環境, 因此,在子類中,就是將子類的變數在父類中執行一遍
superClass.call(this, id)
}
var instance1 = new subClass(10)
var instance2 = new subClass(11)
console.log(instance1.books) // ["javascript", "html", "css", "vue", "react"]
instance2.books.push('設計模式')
console.log(instance2.books) // ["javascript", "html", "css", "vue", "react", "設計模式"]
console.log(instance1.books) // ["javascript", "html", "css", "vue", "react"]
console.log(instance2.id) // 11
console.log(instance1.id) // 10
console.log(instance1)
// 缺點: 只是通過改變this指向,拿到父類的屬性和方法,並沒有繼承父類的prototype
// instance1.showBooks() // instance1.showBooks is not a function
})()
建構函式繼承的缺點: 是通過改變this指向,拿到父類的屬性和方法,並沒有繼承父類的prototype,如果你想直接使用父類建構函式原型上的方法就會報錯,如instance1.showBooks is not a function
- 第三種繼承,組合繼承(集合前兩種繼承的優點,互補)
(function () {
function superClass(name) {
this.books = ['javascript', 'html', 'css', 'vue', 'react'],
this.name = name
}
superClass.prototype.showBooks = function () {
console.log(this.books)
}
function subClass(name, time) {
// call方法可以改變函式的作用環境, 因此,在子類中,就是將子類的變數在父類中執行一遍
superClass.call(this, name)
this.time = time
}
subClass.prototype = new superClass()
subClass.prototype.getTime = function() {
console.log(this.time)
}
var instance1 = new subClass('js book', 2014)
// var instance2 = new subClass('js book', 2014)
instance1.books.push('設計模式')
console.log(instance1.books)
// console.log(instance2.books)
// instance2.showBooks()
})()
組合繼承的缺點,父類建構函式會被執行兩次,在執行call方法時,父類建構函式會執行一次, 在將父類例項賦值給子類原型時,會再次執行一次
- 第四種繼承方案: 原型式繼承
概念: 宣告一個過渡函式物件, 相當於類式繼承中的子類,它是對類式繼承的封裝,只是對類式繼承的增強,但是並沒有解決類式繼承帶來的副作用
(function(){
function inheritObject(o) {
// 宣告一個過渡函式物件, 相當於類式繼承中的子類
// 相對於類式繼承的優點,由於F過渡類中的建構函式中沒有內容,開銷比較小,使用起來也比較方便,但是並沒有解決
// 類式繼承的缺點
function F() {}
F.prototype = o
// 返回過度物件中的例項,該例項的原型繼承了父物件
return new F()
}
var book = {
books: ['javascript', 'html', 'css', 'vue', 'react'],
name: 'book1'
}
var newBook = inheritObject(book)
newBook.name = 'book'
newBook.books.push('設計模式')
console.log(newBook.books) // ["javascript", "html", "css", "vue", "react", "設計模式"]
var newBook1 = inheritObject(book)
newBook1.name = 'book1'
newBook1.books.push('演算法')
console.log(newBook.name) // book
console.log(newBook1.name) // book1
console.log(newBook1.books)// ["javascript", "html", "css", "vue", "react", "設計模式", "演算法"]
})()
- 第五種繼承方式, 寄生式繼承
寄生式繼承, 顧名思義,就像寄生蟲一樣寄託於某個物件內部生長, 是指依託於原型繼承, 並且可以建立自己的屬性和方法
(function(){
function inheritObject(o) {
function F() {}
F.prototype = o
// 返回過度物件中的例項,該例項的原型繼承了父物件
return new F()
}
var book = {
books: ['javascript', 'html', 'css', 'vue', 'react'],
name: 'book1'
}
function createBook(obj) {
var o = inheritObject(obj)
// 基於父物件拓展出的新物件
o.getName = function() {
return this.name
}
return o
}
var newBook = createBook(book)
newBook.name = 'book'
newBook.books.push('設計模式')
console.log(newBook.books) // ["javascript", "html", "css", "vue", "react", "設計模式"]
var newBook1 = createBook(book)
newBook1.name = 'book1'
newBook1.books.push('演算法')
console.log(newBook.getName())
console.log(newBook1.books)// ["javascript", "html", "css", "vue", "react", "設計模式", "演算法"]
})()
- 第六種繼承方式, 寄生組合繼承(終極繼承方式)
(function () {
function inheritObject(o) {
function F() {}
F.prototype = o
// 返回過度物件中的例項,該例項的原型繼承了父物件
return new F()
}
function inheritPrototype(subClass, superClass) {
// 得到父物件的副本
var p = inheritObject(superClass.prototype)
p.constructor = subClass
// 給子類的原型重新賦值為父物件的副本
subClass.prototype = p
// 通常副本通過原型繼承就可以得到,這裡為什麼不直接賦值呢,因為這麼賦值對父類原型物件複製得到的複製物件
// p中的constructor指向的不是subClass的子類物件,因此在此繼承中要對複製物件p做一次增強,修復其constructor指向不正確的問題
// 最後將得到的複製物件p賦值給子類的原型
}
function superClass(name) {
this.name = name
this.books = ['javascript', 'html', 'css', 'vue', 'react']
}
superClass.prototype.getName = function () {
console.log(this.name)
}
function subClass(name, time) {
superClass.call(this, name)
this.time = time
}
inheritPrototype(subClass, superClass)
subClass.prototype.getTime = function () {
console.log(this.time)
}
var newBook1 = new subClass('book', 2014)
var newBook2 = new subClass('book1', 2015)
newBook1.books.push('設計模式')
console.log(newBook1.books) // ["javascript", "html", "css", "vue", "react", "設計模式"]
console.log(newBook2.books) // ["javascript", "html", "css", "vue", "react"]
newBook1.getTime() // 2014
})()
- 第七種繼承方式,es6的class繼承
class 可以理解為function,class寫法只是讓物件原型的寫法更加清晰、更像面向物件程式設計的語法而已,也時基於prototype原型鏈查詢,當呼叫super時,相當於自動執行了call方法,更改子類的this指向,通過呼叫Object.create(),將子類_proto_指向父類
class superClass {
//建構函式,裡面寫上物件的屬性
constructor(props) {
this.books = ['javascript', 'html', 'css', 'vue', 'react']
}
//方法寫在後面
getBooks = function () {
return this.books
}
}
//class繼承
class subClass extends superClass {
//建構函式
constructor(props) {
//props是繼承過來的屬性
//呼叫實現父類的建構函式
super(props) //相當於獲得父類的this指向
}
addBooks(book) {
this.books.push(book)
}
getBooks() {
return this.books
}
}
var newBook1 = new subClass()
var newBook2 = new subClass()
newBook1.addBooks('設計模式')
console.log(newBook1.getBooks()) // (6) ["javascript", "html", "css", "vue", "react", "設計模式"]
console.log(newBook2.getBooks())// (5) ["javascript", "html", "css", "vue", "react"]