js 繼承 原型鏈

這裏先說基於原型鏈實現的繼承。那首先就得明白什麼是原型鏈了;函數

每一個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,而實例都包含一個指向原型對象的內部指針。this

那麼,假如咱們讓原型對象等於另外一個類型的實例,此時的原型對象將包含指向另外一個原型對象的指針,相應地,另外一個原型對象也包含着指向另外一個構造函數的指針。spa

假如另外一個原型又是另外一個類型的實例,那麼上述關係依然成立。層層遞進,就成一條鏈了。prototype

實現原型鏈的基本模式:指針

function SuperType() {對象

  this.property = true;繼承

}原型鏈

SuperType.prototype.getSuperValue = function() {get

  return this.property;原型

}

function SubType() {

  this.subproperty = false;

}

SubType.prototype = new SuperType();  //實現繼承

SubType.prototype.getSubValue = function() {

  return this.subproperty;

}

var instance = new SubType();

alert(instance.getSuperValue); // true

上述SubType經過重寫原型對象的方式繼承了SuperType,因此原來位於SuperType裏的方法,如今也位於SubType裏了,咱們沒有使用SubType默認提供的原型,而是給它換了一個新原型。這個新原型就是SuperType的實例。

因而,新原型不只有做爲SuperType實例全部的屬性和方法,內部還有一個指針,指向了SuperType的原型。

從而,instance指向SubType的原型,SubType的原型又指向SuperType的原型;getSuperValue()的方法仍是在SuperType.prototype中,但property則位於SubType.prototype中,由於property是一個實例屬性,而getSuperValue()則是一個原型方法。既然SubType.prototype如今是SuperType的實例,那麼property固然位於該實例中了

此外,instance.constructor如今指向的是SuperType了,由於原來SubType.prototype指向了SuperType的原型,而這個原型對象的constructor屬性指向的是SuperType

 

默認的原型

咱們知道,全部的引用類型默認都繼承了Object,而這個繼承也是原型鏈實現的。全部的函數的默認原型都是Object的實例,所以默認原型裏面都會包含一個指針,指向Object.prototype

這也是全部自定義類型都會繼承toString(),valueof()等默認方法的根本緣由

 

原型和實例的關係

alert(isntance instanceof Object) ; // true

alert(isntance instanceof SuperType);  // true

alert(isntance instanceof SubType) ; // true

alert(Object.prototype.isPrototypeOf(instance));  // true

alert(SuperType.prototype.isPrototypeOf(instance)); // true

alert(SubType.prototype.isPrototypeOf(instance)); // true

 

經過原型鏈實現繼承,不能使用字面量對象建立原型方法,那樣會重寫原型鏈,切斷原來原型與實例之間的聯繫

原型鏈存在的問題:

(1)最主要的問題是來自包含引用類型值的緣由

舉例:

function SuperType() {

  this.colors = ['red', 'yellow', 'blue'];

}

function SubType() {

}

SubType.prototype = new SuperType();

var instance1 = new SubType();

instance1.colors.push('black');

var instance2 = new SubType();

alert(instance1.colors);  // 'red,yellow,blue,black'

alert(instance2.colors); // 'red,yellow,blue,black'

SuperType的每一個實例都會包含各自的colors屬性,當SubType經過原型鏈繼承以後,SubType.prototype也包含了一個colors屬性,爲全部實例所共享,全部修改instance1,會反映到instance2

(2)另外一個問題:不能向超類型的構造函數中傳遞參數

解決辦法:

1.借用構造函數(經典繼承)

function SuperType () {

  this.colors = ['red', 'blue', 'green'];

}

function SubType() {

  SuperType.call(this)

}

var instance1 = new SubType();

instance1.colors.push('black');

alert(instance1.colors); // "red,blue,green,black"

var instance2 = new SubType();

alert(instance2.colors) // "red,blue,green"

代碼紅色部分借調了超類型的構造函數,經過使用call()方法,咱們其實是在(將來將要)新建立的SubType實例的環境下調用了SuperType構造函數。就會在新SubType對象上執行SuperType函數中定義的全部對象初始化代碼

優點: 傳遞參數,能夠在子類型構造函數中向超類型構造函數傳遞參數

function SuperType (name) {

  this.name = name;

}

function SubType () {

  SuperType.call (this, 'bob');  //繼承SuperType,同時傳遞參數

  this.age = 29;  //實例屬性

}

var instance = new SubType();

alert(instance.name); // 'bob'

alert(instance.age);  //29

問題:方法都在構造函數中定義,沒法複用。並且,在超類型原型中定義的方法,對子類型不可見

2.組合繼承

思路:使用原型鏈實現對原型屬性和方法的繼承,而經過借用構造函數來實現對實例屬性的繼承

function SuperType(name) {

  this.name = name;

  this.colors = ['red', 'blue', 'green'];

}

SuperType.prototype.sayName = function() {

  alert(this.name);

};

function SubType (name, age) {

  SuperType.call(this, name);  //繼承屬性

  this.age = age;

}

// 繼承方法

SubType.prototype = new SuperType();

SubType.prototype.constructor = SubType;

SubType.prototype.sayAge = function () {

  alert(this.age);

};

var instance1 = new SubType('bob', 20);

instance1.colors.push('black'); 

alert(instance1.colors); // "red,blue,green,black"

instance1.sayName();  // 'bob'

instance1.sayAge();  // 20

var instance2 = new SubType('alice', 21);

alert(instance2.colors); // "red,blue,green"

instance2.sayName(); // 'alice'

instance2.sayAge(); // 21

 

3.原型式繼承(object.create())

實現原理

function object(o) {

  function F(){}

  F.prototype = o;

  return new F()  //返回的是構造函數的實例,因此只有_proto_屬性,指向F.prototype

}

舉例:

var person = {

  name: 'bob',

  friends: ['a', 'b', 'c']

}

var anotherPerson = Object.create(person);

anotherPerson.name = 'alice';

anotherPerson.friends.push('d');

 

var yetAnotherPerson = Object.create(person);

yetAnotherPerson.name = 'Linda';

yetAnotherPerson.friends.push('e');

alert(person.friends);  // 'a,b,c,d,e'

在沒有必要興師動衆地建立構造函數,而只想讓一個對象與一個對象保持相似的狀況下,能夠用原型式繼承,不過,

包含引用類型的值的屬性始終都要共享相應的值,就像使用原型模型同樣

 

4.寄生式繼承

 

function createAnother (original) {

  var clone = object.create(original);  // 經過調用函數建立一個新對象

  clone.sayHi = function() {   // 以某種方式加強對象

    alert('Hi');

  }

  return clone;  //返回這個對象

}

var person = {

  name: 'bob',

  friends: ['a', 'b', 'c']

}

var anotherPerson = createAnother(person);

anotherPerson對象具備了person全部屬性和方法, 因爲採用object.create()建立,因此相似於原型式繼承

 

5.寄生組合式繼承

組合式繼承最大的不足是不管什麼狀況下,會調用兩次超類型構造函數;一次是在建立子類型原型的時候,另外一次是在子類型構造函數內部。

看下面例子:

function SuperType(name) {

  this.name = name;

  this.colors = ['red', 'blue', 'green']

}

SuperType.prototype.sayName = function() {

  alert(this.name)

}

function SubType() {

  SuperType.call(this,name);  //第二次調用SuperType()

  this.age = age;

}

SubType.prototype = new SuperType();  //第一次調用SuperType()

SubType.prototype.constructor = SubType; 

SubType.prototype.sayAge = function() {

  alert(this.age);

}

解釋: 第一次調用SuperType()時,將SuperType()的實例屬性賦給了SubType.prototype;至關於在SubType.prototype上建立了兩個屬性name,colors

第二次調用SubType()構造函數時,又會調用SuperType()一次,此次又在新對象上建立了實例屬性name和colors,因而,這兩個屬性就屏蔽了原型中的兩個同名屬性。這樣就在SubType.prototype上建立了多餘的沒必要要的屬性

 

寄生組合式繼承:沒必要爲了指定子類型的原型而調用超類型構造函數,咱們所須要的無非就是超類型原型的一個副本而已。

        本質上,就是利用寄生式繼承來繼承超類型的原型,再將結果指定給子類型原型

function inheritPrototype(subType, superType) {

  var prototype = object.create(superType.prototype);  //建立對象

  prototype.constructor = subType;    // 加強對象

  subType.prototype = prototype;  //指定對象

}

因此寄生式組合繼承:

function SuperType(name) {

  this.name = name;

  this.colors = ['red', 'blue', 'green']

}

SuperType.prototype.sayName = function() {

  alert(this.name)

}

function SubType() {

  SuperType.call(this,name);  

  this.age = age;

}

inheritPrototype(subType, superType);

SubType.prototype.sayAge = function() {

  alert(this.age);

}

相關文章
相關標籤/搜索