JavaScript實現(xiàn)繼承的7種方式總結(jié)
什么是繼承
用官方點的話講繼承是面向?qū)ο笕筇卣髦?,可以使得子類具有父類的屬性和方法,同時還可以在子類中重新定義以及追加屬性和方法。通俗的來說繼承就是子類可以從父類那里復(fù)用一些屬性和方法,從而達(dá)到復(fù)用代碼的一個效果。
實現(xiàn)繼承的6種方式
- 原型鏈繼承
- 構(gòu)造函數(shù)繼承
- 組合繼承(偽經(jīng)典繼承)
- 原型式繼承
- 寄生式繼承
- 寄生組合式繼承
- class 類繼承
1. 原型鏈繼承
function Person() { this.name = 'bruce' this.age = 18 this.gender = '男' this.like = { sport: 'running', book: 'JavaScript' } this.getName = function() { console.log(this.name); } } function Student() { this.school = '新華小學(xué)' } let s = new Student() console.log(s.name); // 未繼承前 undefined Student.prototype = new Person() // 繼承之后 let s1 = new Student() s1.getName() // bruce console.log(s1.like.book); // JavaScript s1.like.book = 'Java' // 修改引用類型的值 s1.name = 'jack' // 修改非引用類型的值 console.log(s1.like.book); // Java s1.getName() // jack console.log('----------'); let s2 = new Student() s2.getName() // bruce console.log(s2.like.book); // Java
所謂的原型鏈繼承,在我的理解就是子類通過原型鏈來繼承到父類的屬性,在上面的代碼中我們也可以看到在子類實例s
沒有繼承到父類的屬性時,子類去訪問父類才有的屬性值為:undefined
。
在將父類實例對象掛載到子類Student構(gòu)造函數(shù)的原型上面后,那么此時子類的原型上面就有了父類的全部屬性。因為Person
的實例對象被顯示的掛載到了Student
的原型上面。所以子類在繼承之后,所有實例化的子類都擁有了跟父類一樣的屬性,于是這樣子類就通過原型鏈實現(xiàn)了繼承。
優(yōu)點: 子類可以繼承到父類的方法。
缺點:
- 會在子類實例上共享父類所有的引用類型數(shù)據(jù),也就是子類的原型被父類屬性覆蓋。
- 子類實例不能給父類構(gòu)造函數(shù)傳參。
function Person(t) { this.property = t } function Student(t) { this.typeproperty = t } Student.prototype = new Person() let s=new Student('a') console.log(s.property) // undefined console.log(s.typeproperty) // a
這樣子類實例傳的參數(shù)就不能給到父類構(gòu)造函數(shù)里面了。
2. 構(gòu)造函數(shù)繼承
function Person(name,age) { this.name = name this.age = age this.like = { sport: 'running', book: 'JavaScript' } this.getName = function () { console.log(this.name); } } Person.prototype.sayHello = function () { console.log('你好'); } function Student(name, age,) { Person.call(this, name, age) // 借用構(gòu)造函數(shù) } let s1 = new Student('bruce',18) s1.getName() // bruce s1.name='jack' s1.getName() // jack s1.like.sport='swimming' console.log(s1.like.sport); // swimming console.log('------'); let s2=new Student('lucy',19) s2.getName() // lucy console.log(s2.like.sport); // running
構(gòu)造函數(shù)繼承就是使用強(qiáng)制改變 this 指向,借用一個構(gòu)造函數(shù)繼承。相較于原型鏈繼承,構(gòu)造函數(shù)繼承不會共享父類上的引用類型數(shù)據(jù),不會互相影響。
缺點:1.不能訪問到父類原型屬性上面的方法和參數(shù),即 Person.prototype
上的都不能訪問到。
Person.prototype.sayHello = function () { // 在 Person 的原型上面加一個方法 console.log('你好'); } let s1 = new Student('bruce',18) s1.sayHello() // s1.sayHello is not a function
3. 組合繼承(偽經(jīng)典繼承)
相較于原型鏈繼承,構(gòu)造函數(shù)繼承好像已經(jīng)非常完美了,但是它還是存在著一個最大的缺點就是:不能繼承到父類的原型上面的屬性。但是原型鏈繼承就好像沒有這個問題,如果將這兩個繼承方式組合起來搭配使用是不是就能解決掉所有問題,于是這就有了組合繼承。
function Person(name, age) { this.name = name this.age = age this.like = { sport: 'running', book: 'JavaScript' } this.getName = function () { console.log(this.name); } } Person.prototype.sayHello = function () { console.log('你好'); } function Student(name, age) { Person.call(this, name, age) // 構(gòu)造函數(shù)繼承 } Student.prototype = new Person() // 原型鏈繼承 let s1 = new Student('bruce', 18) s1.sayHello() // 你好 console.log(s1);
上面先在子類內(nèi)使用構(gòu)造函數(shù)繼承將父類的顯示屬性掛載到子類上面,此時仍然存在著構(gòu)造函數(shù)繼承中子類不能訪問到父類原型上面屬性的缺點。
所以下面配合原型鏈繼承將父類實例掛載到子類的原型上,此時子類就可以訪問父類的原型上的屬性,同時構(gòu)造函數(shù)繼承將子類的原型被父類屬性覆蓋的問題解決。
組合繼承將前面兩種繼承的缺點都補(bǔ)全了,但是它也有缺點:
不足:重復(fù)調(diào)用兩次父類函數(shù)。
4. 原型式繼承
function object(o){ // 參數(shù)為父類 function F(){} F.prototype=o return new F() } const person={ sex:'man', age:20 } let realPerson=object(person) console.log(realPerson.sex);// man
通過在一個構(gòu)造函數(shù)的原型上掛載父類,此時再將這個掛載了父類的構(gòu)造函數(shù)實例返回出來,那么這個實例的隱式原型就為這個父類,所以此時子類實例對象可以訪問到父類上的屬性,這就是原型式繼承。這個看起來跟理解起來是不是跟原型鏈繼承很像,也挺好理解的。
缺點: 如果父類屬性有引用類型,那么這個引用類型也會被共享出來。
function object(o){ function F(){} F.prototype=o return new F() } const person={ sex:'man', age:20, like:{ sports:'running' } } let r1=object(person) console.log(r1.like);// { sports: 'running' } let r2=object(person) r2.like.sports='singing' console.log(r1.like); // { sports: 'singing' }
實例化兩個不同的對象,更改 r2 的sports 屬性,結(jié)果 r1 的 sports 屬性也被修改。
5. 寄生式繼承
這種繼承方式跟原型式繼承有著異曲同工之妙,只不過可以增強(qiáng)這個父類。其存在的缺點也與原型式繼承一樣。
function createPerson(original) { var clone = Object.create(original) clone.say = function() { // 增強(qiáng)這個對象 console.log('hello'); } return clone }
6.寄生組合式繼承
這個是一個比較完美的繼承方式,使用組合繼承與寄生繼承。
function Person(name, age) { this.name = name this.age = age this.like = { sport: 'running', book: 'JavaScript' } this.getName = function () { console.log(this.name); } } Person.prototype.sayHello = function () { console.log('你好'); } function Student(name, age) { Person.call(this, name, age) // 借用構(gòu)造函數(shù) } const Fn=function(){} Fn.prototype=Person.prototype Student.prototype = new Fn() let s1 = new Student('bruce', 18) s1.sayHello() // 你好 console.log(s1);
這種繼承方式結(jié)合了組合繼承,借助一個空的構(gòu)造函數(shù),將父類的原型掛載到這個空的構(gòu)造函數(shù)上面,然后將其附在子類的原型上面,這樣就解決了組合式繼承的缺點。
7.class 類繼承
Class 可以通過extends
關(guān)鍵字實現(xiàn)繼承,讓子類繼承父類的屬性和方法。
class Person{ constructor(name){ this.name=name } } class Student extends Person{ constructor(name,age){ super(name) this.age=age } } let s=new Student('bruce',20) console.log(s); //Student { name: 'bruce', age: 20 }
以上就是JavaScript實現(xiàn)繼承的7種方式總結(jié)的詳細(xì)內(nèi)容,更多關(guān)于JavaScript繼承的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaScript鼠標(biāo)禁止右鍵禁止打開控制臺及鍵盤禁用
這篇文章主要給大家介紹了關(guān)于JavaScript鼠標(biāo)禁止右鍵禁止打開控制臺及鍵盤禁用的相關(guān)資料,實現(xiàn)禁止右鍵和禁止打開控制臺是一種常見的網(wǎng)頁保護(hù)技巧,可以防止非法復(fù)制、盜取網(wǎng)頁資源等安全問題,需要的朋友可以參考下2023-10-10document.selection.createRange方法與實例
document.selection.createRange() 根據(jù)當(dāng)前文字選擇返回 TextRange 對象,或根據(jù)控件選擇返回 ControlRange 對象2006-10-10淺談nodeName,nodeValue,nodeType,typeof 的區(qū)別
本文主要簡單介紹了nodeName,nodeValue,nodeType,typeof 的區(qū)別,算是知識點的一個小總結(jié),希望對小伙伴們有所幫助2015-01-01JavaScript 監(jiān)聽組合按鍵思路及代碼實現(xiàn)
這篇文章主要介紹了JavaScript 監(jiān)聽組合按鍵思路及代碼實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07一個檢測表單數(shù)據(jù)的JavaScript實例
這篇文章主要介紹了一個檢測表單數(shù)據(jù)的JavaScript實例,很簡單,很實用,比較適合初學(xué)者2014-10-10