js 繼承

 

構造函數繼承
  類式繼承是在函數對象內調用父類的構造函數,使得自身得到父類的方法和屬性。call和apply方法爲類式繼承提供了支持。經過改變this的做用環境,使得子類自己具備父類的各類屬性。
只繼承構造函數的屬性,不繼承原型的屬性。 解決原型鏈缺點。能夠繼承多個構造函數的屬性,在子實例中能夠向父類傳參。
缺點:沒法實現構造函數的複用;每一個新實例都有父類構造函數副本,臃腫。
var father = function() {
  this.age = 52;
  this.say = function() {
    alert('hello i am '+ this.name ' and i am '+this.age + 'years old');
  }
}
var child = function() {
  this.name = 'bill';
  father.call(this);
}
var man = new child();
man.say();
原型鏈繼承(借用原型鏈實現繼承)
function Parent1(){ this.name = "parent1"; this.colors = ["red","blue","yellow"]; } function Child1(){ this.name = "child1"; } Child1.prototype = new Parent1();//把子類的prototype設置爲父類的實例
實例可繼承的屬性有:實例構造函數的屬性,父類構造函數屬性,父類原型的屬性,新實例不會繼承父類實例的屬性。
優勢:繼承了父類的模板,又繼承了父類的原型對象,
缺點:1 若是屬性是引用類型的話,會共享引用類型。
2 新實例沒法向父類構造函數傳參,繼承單一。父類實例傳參,不是子類實例化傳參。
 
組合式繼承
1 能夠繼承父類原型上的屬性,能夠傳參,能夠服用。
2 每一個新實例引入構造函數屬性上私有的。
缺點:調用兩次父類構造函數,耗內存。子類的構造函數會代替原型上的構造函數。
 
 
這裏所謂的組合是指組合借用構造函數和原型鏈繼承兩種方式。
function Parent2(){ this.name = "parent2"; this.colors = ["red","blue","yellow"]; } function Child2(){ Parent2.call(this); this.type = "child2"; } Child2.prototype = new Parent2()
注意第6,9行,這種方式結合了借用構造函數繼承和原型鏈繼承的有點,可否解決上述兩個實例對象沒有被隔離的問題呢?
var s1 = new Child2(); s1.colors.push("black"); var s2 = new Child2(); s1.colors; // (4) ["red", "blue", "yellow", "balck"] s2.colors; // (3) ["red", "blue", "yellow"]
能夠看到,s2和s1兩個實例對象已經被隔離了。
但這種方式仍有缺點。父類的構造函數被執行了兩次,第一次是Child2.prototype = new Parent2(),第二次是在實例化的時候,這是沒有必要的。
組合式繼承優化1
直接把父類的原型對象賦給子類的原型對象
function Parent3(){ this.name = "parent3"; this.colors = ["red","blue","yellow"]; } Parent3.prototype.sex = "男"; Parent3.prototype.say = function(){console.log("Oh, My God!")} function Child3(){ Parent3.call(this); this.type = "child3"; } Child3.prototype = Parent3.prototype; var s1 = new Child3(); var s2 = new Child3(); console.log(s1, s2);
可是,咱們來看以下代碼:
console.log(s1 instanceof Child3); // true console.log(s1 instanceof Parent3); // true
能夠看到,咱們沒法區分實例對象s1究竟是由Child3直接實例化的仍是Parent3直接實例化的。用instanceof關鍵字來判斷是不是某個對象的實例就基本無效了。
咱們還能夠用.constructor來觀察對象是否是某個類的實例:
console.log(s1.constructor.name); // Parent3
從這裏能夠看到,s1的構造函數竟然是父類,而不是子類Child3,這顯然不是咱們想要的。
組合式繼承優化2
這是繼承的最完美方式
function Parent4(){ this.name = "parent4"; this.colors = ["red","blue","yellow"]; } Parent4.prototype.sex = "男"; Parent4.prototype.say = function(){console.log("Oh, My God!")} function Child4(){ Parent4.call(this); this.type = "child4"; } Child4.prototype = Object.create(Parent4.prototype); Child4.prototype.constructor = Child4;
Object.create是一種建立對象的方式,它會建立一箇中間對象
var p = {name: "p"} var obj = Object.create(p) // Object.create({ name: "p" })
經過這種方式建立對象,新建立的對象obj的原型就是p,同時obj也擁有了屬性name,這個新建立的中間對象的原型對象就是它的參數。
這種方式解決了上面的全部問題,是繼承的最完美實現方式。
ES6中繼承
Class 能夠經過extends關鍵字實現繼承,這比 ES5 的經過修改原型鏈實現繼承,要清晰和方便不少。
class Parent { } class Child1 extends Parent { constructor(x, y, colors) { super(x, y); // 調用父類的constructor(x, y) this.colors = colors; } toString() { return this.colors + ' ' + super.toString(); // 調用父類的toString() } }
上面代碼中,constructor方法和toString方法之中,都出現了super關鍵字,它在這裏表示父類的構造函數,用來新建父類的this對象。
子類必須在constructor方法中調用super方法,不然新建實例時會報錯。若是子類沒有定義constructor方法,這個方法會被默認添加,無論有沒有顯式定義,任何一個子類都有constructor方法。
ES5 的繼承,實質是先創造子類的實例對象this,而後再將父類的方法添加到this上面(Parent.apply(this))。
ES6 的繼承機制徹底不一樣,實質是先創造父類的實例對象this(因此必須先調用super方法),而後再用子類的構造函數修改this。
相關文章
相關標籤/搜索