js從誕生之初本就不是物件導向的語言。如何在js中實現繼承,總結而言會有四種寫法。
function animal(name)
}
function dog(name, hobby)
}this.hobby = hobby
}
let dog1 = new dog('xiaohei', 'bone')
let dog2 = new dog('fofo', 'bone and fish')
console.log(dog1.sayname()) // xiaohei
console.log(dog2.sayname()) // fofo
複製**
通過物件冒充實現繼承,實際上是在建構函式中,通過獲取父類中的所有屬性,並儲存到自身物件中,這樣則可以呼叫父類的屬性和方法了。這裡forin的方式遍歷父類屬性,因為forin會遍歷公開的屬性和方法,所以通過hasownproperty
控制寫入當前物件的範圍。否則則會將所有屬性全部變為私有屬性。
這樣做有乙個缺點就是,無法訪問父類中的公開方法和屬性(prototype中的方法)
animal.prototype.sayhobby = function()
dog1.sayhobby() // vm2748:1 uncaught typeerror: dog1.sayhobby is not a function at :1:6
複製**
**優化
function animal(name)
}
function dog(name, hobby)
let dog1 = new dog('xiaohei', 'bone')
let dog2 = new dog('fofo', 'bone and fish')
console.log(dog1.sayname()) // xiaohei
console.log(dog2.sayname()) // fofo
複製**
function animal(name)
}animal.prototype.sayname = function()
function dog(name)
dog.prototype = new animal('animal')
let dog1 = new dog('xiaohei')
dog1.sayname() // xiaohei
let dog2 = new dog('feifei')
dog2.sayname() // feifei
複製**
這種繼承方式是通過對子類的prototype.__proto__
引用父類的prototype
,從而可以讓子類訪問父類中的私有方法和公有方法。詳情可以檢視關鍵字new
的實現。
類式繼承會有兩方面的缺點
引用陷阱-子類物件可以隨意修改父類中的方法和變數,並影響其他子類物件
dog1.types.push('fish')
console.log(dog1.types) // ["cat", "dog", "fish"]
console.log(dog2.types) // ["cat", "dog", "fish"]
複製**
無法初始化構造不同的例項屬性
這個主要是由於類式繼承,是通過dog.prototype = new animal('animal')
實現的,我們只會呼叫一次父類的建構函式。所以只能在子類中從寫父類的屬性,如上的name
屬性,在子類中需要重寫一次。
組合繼承,即結合以上兩種繼承方式的優點,拋棄兩者的缺點,而實現的一種組合方式
function animal(name)
animal.prototype.sayname = function()
function dog(name, hobby)
// 子類例項可以訪問父類prototype的方法和屬性
dog.prototype = new animal()
dog.prototype.constructor = dog
dog.prototype.sayhobby = function()
// test instance of dog1
let dog1 = new dog('xiaohei', 'bone')
dog1.sayname() // xiaohei
dog1.sayhobby() // bone
dog1.types.push('ant') // types: ['dog', 'cat', 'ant']
// test instance of dog2
let dog2 = new dog('feifei', 'fish')
dog2.sayname() // feifei
dog2.sayhobby() // fish
dog2.types // ['dog', 'cat']
複製**
組合模式,解決了使用建構函式繼承和類式繼承帶來的問題,算是一種比較理想的解決繼承方式,但是這裡還有一些瑕疵,呼叫了兩次父類(animal)的建構函式。
所以為了解決這個問題,進行了優化,產生了?這種繼承方式
function animal(name)
animal.prototype.sayname = function()
function dog(name, hobby)
/**注意下面這兩行****/
dog.prototype = object.create(animal.prototype)
// 由於對animal.prototype進行了淺拷貝,則改變了dog中的建構函式,所以需要重新賦值dog為建構函式
dog.prototype.constructor = dog
dog.prototype.sayhobby = function()
// test instance of dog1
let dog1 = new dog('xiaohei', 'bone')
dog1.sayname() // xiaohei
dog1.sayhobby() // bone
dog1.types.push('ant') // types: ['dog', 'cat', 'ant']
// test instance of dog2
let dog2 = new dog('feifei', 'fish')
dog2.sayname() // feifei
dog2.sayhobby() // fish
dog2.types // ['dog', 'cat']
複製**
mdn解釋:object.create()方法建立乙個新物件,使用現有的物件來提供新建立的物件的__proto__。可以理解為:使用object.create()進行一次淺拷貝,將父類原型上的方法拷貝後賦給dog.prototype,這樣子類上就能擁有了父類的共有方法,而且少了一次呼叫父類的建構函式。
重寫create
方法:
function create(target)
f.prototype = target
return new f()
}複製**
同時需要注意子類的constructor,由於更改了子類的prototype,所以需要重新設定子類的建構函式。
如果之前有學習過,或者有物件導向語言基礎的,這個則很容易理解,使用extens關鍵字作為繼承。
class animal
sayname()
}class dog extends animal
sayhobby()
}let dog1 = new dog('xiaohei', 'bone')
dog1.sayname() // xiaohei
dog1.sayhobby() // bone
let dog2 = new dog('feifei', 'fish')
dog2.sayname() // feifei
dog2.sayhobby() // fish
複製**
綜上所述,js中的繼承總共分為構造器繼承,類式繼承,組合繼承,組合寄生繼承,es6中extends的繼承五種繼承方式,其中第四種是第三種的優化實現。
最後,實現new
關鍵字的實現
mdn: new 運算子建立乙個使用者定義的物件型別的例項或具有建構函式的內建物件的例項。
語法:new constructor[([arguments])]
function new(constructor, arguments)
if (constructor && typeof constructor === 'function')
}複製**
JS中繼承總結
一 原型繼承 父類 function persion name,age 父類的原型物件屬性 persion.prototype.id 6609 子類 function boy 繼承實現 子類的原型物件屬性 new 父類 父類的例項物件 boy.prototype new persion zl 27 ...
js 中繼承的幾種方式
繼承的方式一共有三種 一 原型繼承 通過prototype 來實現繼承。function person name,age person.prototype.sayhello function var per new person 馬小倩 21 per.sayhello 輸出 使用原型得到name 馬...
js 中繼承的幾種方式
繼承的方式一共有三種 一 原型繼承 通過prototype 來實現繼承。function person name,age person.prototype.sayhello function var per new person 馬小倩 21 per.sayhello 輸出 使用原型得到name 馬...