對象的建立繼承

一.建立一個對象

原由.................
用Object或對象字面量建立對象會產生大量的重複代碼,並且只能經過變量名來區分不一樣的對象,沒有記錄對象的類型
例如:函數

//Object建立對象
 var student = new Object();
 student.name = 's';
 student.age = "20";
 var student1 = new Object();
 student1.name = "s1";
 student.age = "20";
 //字面量建立對象
 var student = {
   name: 's',
   age: '23'
 }
 var student1 = {
   name: 's',
   age: '23'
 }

1.工廠模式建立對象

工廠模式解決了類似代碼重複書寫的問題this

//好處:js中沒法建立類,爲了模仿類的建立方式,抽象具體對象的建立過程;
//缺點:工廠模式建立的對象是經過普通函數調用實現的,沒有解決標識對象類型的問題,沒法區分對象的類型,
  function person(name) {
   var o = new Object();
   o.name = name;
   o.sayName = function() {
     console.log(this.name)
   }
   return o;
 }
 var p1 = person('a');
 var p2 = person('b');

2.構造函數模式建立對象

function Person(name) {
   this.name = name;
   this.sayName = function() {
     console.log(this.name);
   }
//function() { console.log(this.name);}至關於new Function(){console.log(this.name);}}
 var a = new Person('ww');
 a.sayName();
 console.log(a)

new 一個實例的過程:
(1)先建立一個對象
(2)將構造函數的做用域賦給新對象
(3),執行構造函數中的代碼
(4)返回新對象spa

構造函數模式解決了標識構造函數類型的問題,可是構造函數內部每定義一個函數就會實例化一個對象,能夠經過將公用的方法提到全局環境下,經過普通函數調用的方式實現調用函數,避免了重複實例化對象的問題prototype

function Person(name) {
   this.name = name;
   this.sayName = sayName;
 }

 function sayName() {
   console.log(this.name)
 }
 var a = new Person('p1');
 var b = new Person('p2');
 a.sayName();
 b.sayName();

若是Person內部封裝較多的函數,會致使定義大量的全局函數,這些函數散亂分佈在全局環境中,失去了封裝性.爲了解決這個問題,能夠用原型模式建立對象設計

3.原型模式

原型模式建立對象,把屬性和函數添加到對象的prototype上,實例化一個對象p1,p1能夠經過原型鏈訪問到原型鏈上的對象code

function Person() {}
 Person.prototype.name = "ww";
 Person.prototype.sayName = function() {
   console.log(this.name);
 }
 var p1 = new Person();
 p1.sayName();

(1)原型模式的另外一種方式:
clipboard.png
這種方式會致使,Person.prototype上本來指向Person的constructor丟失,能夠手動添加constructor屬性,以下
clipboard.png對象

(2)若是先建立實例,再定義Person.prototyp的值會報錯,p1仍是經過[[prototype]]隱式屬性指向Person沒修改過的原型,致使找不到sayName方法
clipboard.png繼承

解釋如js高級程序設計一書中的例子:
clipboard.png
clipboard.pngip

(3)原型鏈模式存在如下問題:(1),prototype上的屬性和方法共享,一個對象對prototype屬性的修改會影響另外一個對象的屬性;(2)不能傳遞參數原型鏈

clipboard.png

4.組合使用構造函數和原型函數

咱們能夠把一些共享的屬性和方法添加到prototype,再利用構造函數在實例對象上添加不一樣屬性

clipboard.png

二.繼承

1.原型鏈繼承

經過將一個構造函數的原型從新賦值(另外一個構造函數的實例)實現繼承
例子:

function SuperType() {
  this.property = ['p1'];
}
SuperType.prototype.getValue = function() {
  console.log(this.property);
}

function SubType() {
  this.subproperty = ['1'];
}
SubType.prototype.getSubValue = function() {
  console.log("getSubVlue", this.subproperty);
}
SubType.prototype = new SuperType();
var a = new SubType();
console.log("a", a);

結果:
clipboard.png

對於引用類型屬性,原型鏈繼承會將該屬性做爲公共屬性,誰均可以對它的值進行修改;對於像name這樣的非引用類型,每建立一個實例就會定義一個新額屬性,不會和其餘實例中的屬性共享,以下所示

clipboard.png

2.借用構造函數

爲了解決引用類型值共享的問題和原型鏈繼承不能傳遞參數的缺陷,能夠在子類型構造函數的內部調用超類型構造函數。以下:

function SuperType() {
  this.property = ['p1'];
}
function SubType() {
  this.subproperty = ['1'];
  SuperType.call(this);
}
SubType.prototype = new SuperType();
var a = new SubType();
var b = new SubType();
a.property.push('2');
console.log(a.property, b.property);

clipboard.png

利用單純的借用構造函數解決了引用類型值共享的問題,可是若是大量的函數寫着超類中,函數沒法複用,全部須要結合原型構造函數.

3.組合構造函數

組合構造函數到的思路是將利用構造函數實例實現對屬性的繼承,利用原型鏈來實現對原型對象的屬性和函數的繼承.

clipboard.png

因爲給SubType.prototype直接賦值爲SuperType的實例,致使constructor丟失,利用Object.defineProperty找回

clipboard.png

4.原型式繼承

思路:基於已有的對象建立新對象,繼承一個現成的對象

clipboard.png

clipboard.png
原型式繼承存在的問題就和原型模式同樣,對於引用型屬性有共享的特性

5.寄生式繼承

思路:建立一個僅用於封裝繼承過程的函數

clipboard.png

該繼承方式存在的問題是a對象內部的函數不能複用

6.寄生組合繼承

組合構造函數也有缺陷,須要調用兩次超類構造函數,下降效率

function SuperType(name) {
  this.name = name;
  this.books = ['b1']
}

SuperType.prototype.getName = function() {
  console.log(this.name);
}

function SubType(name, age) {
  this.age = age;
  SuperType.call(this, name); //第二次調用構造函數
}

SubType.prototype = new SuperType(); //第一次調用構造函數
SubType.prototype.constructor = SubType;
SubType.prototype.getSubValue = function() {
  console.log("getSubVlue", this.age);
}

利用寄生式繼承來繼承超類的原型,利用構造函數繼承實例屬性

clipboard.png

有不對或者表達不許確的地方歡迎指出!

相關文章
相關標籤/搜索