javascript
JS-继承(es5,es6)
首先需要了解原型鏈機(jī)制: 原型鏈作為實(shí)現(xiàn)繼承的主要方法,其基本思想就是利用原型讓一個(gè)引用類型繼承另 一個(gè)引用類型的屬性和方法.
構(gòu)造函數(shù)、原型、實(shí)例之間的關(guān)系: 每個(gè)構(gòu)造函數(shù)都有一個(gè)原型對象(prototype),原型對象都包含一個(gè)指向構(gòu)造函數(shù)的指針(constructor),而實(shí)例都包含一個(gè)指向原型對象的內(nèi)部指針(__propto__).
自我理解: 其實(shí)每個(gè)Function都是Object基類的一個(gè)實(shí)例,所以每個(gè)Function上都有一個(gè)__proto__指向了Object.prototype.當(dāng)查找一個(gè)實(shí)例的屬性時(shí),會(huì)先從這個(gè)實(shí)例的自定義屬性上找,如果沒有的話通過__proto__去實(shí)例所屬類的原型上去找,如果還沒有的話再通過原型(原型也是對象,只要是對象就有__proto__屬性)的__proto__到Object的原型上去找,一級一級的找,如果沒有就undefined(Object的__proto__返回undefined).
(一) 原型鏈繼承 :function Parent(name) { this.name = name;}Parent.prototype.printName = function() {console.log('parent name:', this.name); } function Child(name) {this.name = name; } Child.prototype = new Parent('father'); Child.prototype.constructor = Child;//由于Child.prototype繼承Parent,導(dǎo)致constructor丟失 Child.prototype.printName = function() {console.log('child name:', this.name); } var child = new Child('son'); child.sayName(); // child name: son
這種方法存在兩個(gè)缺點(diǎn):
1.子類型無法給超類型傳遞參數(shù); 2.Child.prototype.sayName 必須寫在 Child.prototype = new Parent('father'); 之后,不然就會(huì)被覆蓋掉。 (二) 類式繼承:function Parent(name) { this.name = name;}Parent.prototype.printName = function() {console.log('parent name:', this.name);}Parent.prototype.doSomthing = function() {console.log('parent do something!');} function Child(name, parentName) {Parent.call(this, parentName);this.name = name; } Child.prototype.printName = function() {console.log('child name:', this.name); } var child = new Child('son'); child.printName(); // child name: son child.doSomthing(); // TypeError: child.doSomthing is not a function
相當(dāng)于 Parent 這個(gè)函數(shù)在 Child 函數(shù)中執(zhí)行了一遍,并且將所有與 this 綁定的變量都切換到了 Child 上,這樣就克服了第一種方式帶來的問題。
缺點(diǎn):沒有原型,每次創(chuàng)建一個(gè) Child 實(shí)例對象時(shí)候都需要執(zhí)行一遍 Parent 函數(shù),無法復(fù)用一些公用函數(shù)。
function Parent(name) {this.name = name; }Parent.prototype.printName = function() {console.log('parent name:', this.name); } Parent.prototype.doSomething = function() {console.log('parent do something!'); } function Child(name, parentName) {Parent.call(this, parentName);// 第二次調(diào)用this.name = name; }Child.prototype = new Parent();// 第一次調(diào)用 Child.prototype.constructor = Child; Child.prototype.printName = function() {console.log('child name:', this.name); }var child = new Child('son'); child.printName(); // child name: son child.doSomething(); // parent do something!
組合式繼承是比較常用的一種繼承方法,其背后的思路是使用原型鏈實(shí)現(xiàn)對原型屬性和方法的繼承,而通過借用構(gòu)造函數(shù)來實(shí)現(xiàn)對實(shí)例屬性的繼承。
這樣,既通過在原型上定義方法實(shí)現(xiàn)了函數(shù)復(fù)用,又保證每個(gè)實(shí)例都有它自己的屬性。
組合式繼承是 JS 最常用的繼承模式,但組合繼承使用過程中會(huì)被調(diào)用兩次:一次是創(chuàng)建子類型的時(shí)候,另一次是在子類型構(gòu)造函數(shù)的內(nèi)部。
第一次調(diào)用構(gòu)造函數(shù)顯然是沒有必要的,因?yàn)榈谝淮握{(diào)用構(gòu)造函數(shù)時(shí)候不需要函數(shù)內(nèi)部的那些實(shí)例屬性,這么寫只是想獲得其原型上的方法罷了,所以這時(shí)候你可能會(huì)這樣寫:
Child.prototype = Parent.prototype;
這樣寫顯然是不對的:
1.首先,你這樣寫的話相當(dāng)于是子類和父類都指向同一個(gè)對象,這時(shí)候如果你添加了新的方法給 Child 但實(shí)際上 Parent 并不需要,相當(dāng)于強(qiáng)行給 Parent 添加了一個(gè)未知的方法。
2.其次,仔細(xì)想想,這樣體現(xiàn)不出繼承的多態(tài)性,比如此時(shí)子類想要重寫父類的 getName 的方法,那么父類的方法也就會(huì)隨之修改,這顯然違背了多態(tài)性。
也就是說我們第一次調(diào)用構(gòu)造函數(shù)的時(shí)候,其實(shí)是不管構(gòu)造函數(shù)里面的內(nèi)容,這是我們可以new一個(gè)空函數(shù),將其prototype指向Parent.prototype,代碼如下:
(四) 寄生組合式繼承:function Parent(name) {this.name = name; } Parent.prototype.printName = function() {console.log('parent name:', this.name); }function Child(name, parentName) {Parent.call(this, parentName); this.name = name; }function inheritPrototype(Parent, Child) {Child.prototype = Object.create(Parent.prototype); //修改Child.prototype.constructor = Child; }inheritPrototype(Parent, Child); Child.prototype.printName = function() {console.log('child name:', this.name); } Child.prototype.constructor = Child;var parent = new Parent('father'); parent.printName(); // parent name: fathervar child = new Child('son', 'father'); child.printName(); // child name: son (五) ES 6 繼承:
class Parent {constructor(name) {this.name = name;}doSomething() {console.log('parent do something!');}printName() {console.log('parent name:', this.name);} }class Child extends Parent {constructor(name, parentName) {super(parentName);this.name = name;}printName() {console.log('child name:', this.name);} } const child = new Child('son', 'father'); child.printName(); // child name: son child.doSomething(); // parent do something! const parent = new Parent('father'); parent.printName(); // parent name: father
總結(jié)
以上是生活随笔為你收集整理的JS-继承(es5,es6)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 前端基础21:正则基础
- 下一篇: javascript一些基础用法