ES5與ES6的繼承

JavaScript自己是一種神馬語言:java

提到繼承,咱們經常會聯想到C#、java等面向對象的高級語言(固然還有C++),由於存在類的概念使得這些語言在實際的使用中抽象成爲一個對象,即面向對象。JavaScript這門語言自己就是做爲瀏覽器腳本語言的弱語言,伴隨着沒有類的概念,JavaScript就成爲了一種基於對象的語言而不是面向對象的語言,面向對象就會存在繼承,那麼基於對象的JavaScript是如何繼承的。數組

ES5規則瀏覽器

JavaScript的4種繼承方式:app

(1)原型繼承函數

栗子:優化

 1 function Animal() {
 2     this.name = "Animal";
 3     this.actions = ['eat', 'drink']
 4     this.eat = function () {
 5         console.log('eat');
 6     };
 7 }
 8 
 9 Animal.prototype = {
10     age: '6years',
11     hobbies: []
12 }
13 
14 function Dog() {
15     //do someting
16 }
17 Dog.prototype = new Animal();
18 
19 function Cat() {
20     //do someting
21 }
22 Cat.prototype = new Animal();
23 
24 var dog = new Dog();
25 var cat = new Cat();

結果:this

能夠看到cat跟dog都繼承了animal的實例對象,原型繼承原型繼承是最基礎的繼承方式,核心就是重寫子類原型,是父類實例對象充當子類原型。spa

若是此時做以下操做會有什麼狀況發生prototype

1 dog.name = 'dog'
2 dog.actions.push('look');
3 dog.age = '7years';
4 dog.hobbies.push('sleep');

cat的結果:code

結果已經很明顯了,原型上的引用類型會被共享。

緣由就是操做數組時,首先會在對象下找當前數組,若是有就會更改對象下的數組,若是沒有就會到原型裏面找數組,因爲dog跟cat的原型是同一個animal因此修改的就是同一個數組,若是是簡單類型,查找對象內沒有此屬性,從新生成一個屬性,而且不會繼續使用原型內部的屬性,即原型共享。

結論:優勢:最基本的繼承方式,簡單;缺點:原型中的引用類型共享。

到這裏是否是有一種原型鏈的感受了呢~

(2)改變上下文的繼承

 1 function Animal(name) {
 2     this.name = name;
 3     this.actions = ['eat', 'drink']
 4     this.eat = function () {
 5         console.log('eat');
 6     };
 7 }
 8 
 9 function Dog() {
10     Animal.call(this, 'dog')
11 }
12 
13 function Cat() {
14     Animal.call(this, 'cat')
15 }
16 
17 var dog = new Dog();
18 var cat = new Cat();

結果:

利用call(apply,bind)方式改變了Animal函數內部this的指向,使this指向分別指向了Dog和Cat

優勢:摒棄了原型,避免了原型共享;解決了向父類構造函數傳參的問題。

缺點:沒生成一個新對象,都會從新定義一次function,嚴重影響內存。

這裏是否是有一種多態的感受了呢~

(3)把前兩種結合起來繼承:

栗子:

 1 function Animal(name) {
 2     this.name = name;
 3     this.actions = ['eat', 'drink']
 4 }
 5 
 6 Animal.prototype = {
 7     eat: function () {
 8         console.log('eat');
 9     }
10 }
11 
12 function Dog(name) {
13     Animal.call(this, name)
14 }
15 Dog.prototype = new Animal();
16 
17 function Cat(name) {
18     Animal.call(this, name)
19 }
20 Cat.prototype = new Animal();
21 
22 var dog = new Dog('dog');
23 var cat = new Cat('cat');

結果:

優勢:這種方式成功的避免了重複定義function的尷尬狀況,同時解決了原型共享的問題。

缺點:若是有兩個子類繼承父類,可是父類的屬性有一個子類不用,怎麼搞?這個是無法避免的,並且父類的屬性所有在子類的原型上,很不美觀。

這裏是否是又彷彿看見了new的原理了呢~

(4)寄生組合繼承:

爲了扣掉組合繼承中原型中不須要的屬性,看到爲了知足這一點,可不能夠介樣:

 1 function Animal(name) {
 2     this.name = name;
 3     this.actions = ['eat', 'drink']
 4 }
 5 
 6 Animal.prototype = {
 7     eat: function () {
 8         console.log('eat');
 9     }
10 }
11 
12 function Dog(name) {
13     Animal.call(this, name)
14 }
15 Dog.prototype = Animal.prototype;
16 
17 function Cat(name) {
18     Animal.call(this, name)
19 }
20 Cat.prototype = Animal.prototype;
21 
22 var dog = new Dog('dog');
23 var cat = new Cat('cat');

結果:

是否是達到了原型中的屬性被消滅的效果了呢。這裏咱們能夠聯想到什麼呢,那就是js的new關鍵字

回顧一下:

1 var
2     Demo = function () {
3         var
4             self = this;
5     };
6 
7 var demo = {};
8 demo.__proto__ = Demo.prototype;
9 Demo.call(demo);

區別在於將Demo.prototype是給對象的原型賦值,一個是給方法的原型賦值。

接着上面的栗子來,乍一看好像對,實際Child中的__proto__爲Object,並非Parent,已經背離了Child繼承Parent的目的。爲啥呢?由於prototype就是Object,js裏一切皆爲對象。

咱們能夠本身控制對象的原型

改進:

 1 function Animal(name) {
 2     this.name = name;
 3     this.actions = ['eat', 'drink']
 4 }
 5 
 6 Animal.prototype.eat = function () {
 7     console.log('eat');
 8 }
 9 
10 function Dog(name) {
11     Animal.call(this, name)
12 }
13 
14 function Cat(name) {
15     Animal.call(this, name)
16 }
17 
18 function initObject(obj) {
19     var
20         F = function () { };
21     F.prototype = obj;
22     return new F();
23 }
24 
25 var
26     dogPrototype = initObject(Animal.prototype),
27     catPrototype = initObject(Animal.prototype);
28 dogPrototype.constructor = Dog;
29 catPrototype.constructor = Cat;
30 Dog.prototype = dogPrototype;
31 Cat.prototype = catPrototype;
32 var dog = new Dog('dog');
33 var cat = new Cat('cat');

結果:

能夠看到cat與dog的原型已是Animal了。ES5 over~

ES6規則

 1 class Animal {
 2     constructor(name) {
 3         this.name = name;
 4     };
 5 
 6     eat() {
 7         console.log('eat')
 8     };
 9 }
10 
11 class Dog extends Animal {
12     constructor() {
13         super();
14     };
15 
16     eat() {
17         super.eat();
18     };
19 }
20 var dog = new Dog();

ES6很大程度優化了ES5的繼承方式,並且constructor也暴露出來,利用super能夠直接調用父級函數以及屬性,至關地方便。

相關文章
相關標籤/搜索