說清楚javascript面向對象、原型、繼承

這是兩年前寫的筆記,本身都有遺忘,相信也會有人不明白,貼在這裏。
都是紅寶書上的內容,在比較難理解的地方加了一些示例和說明。函數

es中定義對象爲:無需屬性的集合、其屬性能夠包含基本值、對象或者函數。this

建立對象

Object實例

建立對象最簡單的方法是建立一個Object的實例。prototype

var person =new Object();
person.name="Mike";
person.age=12;
person.job="student";
person.sayName=function(){
};

對象字面量

var person={
      name:"mike",
      age:10,
      job:"student",
      sayName:function(){
        alert(this.name);
      }
 };

Object實例和對象字面量均可以用來建立單個對象,
可是當要建立多個對象就會產生大量重複的代碼,由此產生了工廠模式。指針

工廠模式

工廠模式的特色是用函數來封裝建立對象的細節,對外以接口的方式調用。code

function createPerson(name,age,job){
      var obj = new Object();
      obj.name=name;
      obj.age=age;
      obj.job=job;
      obj.sayName=function(){
        alert(this.name);
      }
      return obj;
 }
 var person1=createPerson("Mike",12,"student");

工廠模式雖然解決了建立類似對象重複代碼的問題,
但同時還存在一個問題:如何知道對象的類別?由此產生構造函數模式對象

構造函數模式

function CreatePerson(name,age,job){
      this.name=name;
      this.age=age;
      this.job=job;
      this.sayName=function(){
        alert(this.name);
      }
 }
  var person1= new CreatePerson("Mi",13,"student");
  var person2= new CreatePerson("Mike",12,"student");
  alert(person2.constructor==CreatePerson);//true
  alert(person1 instanceof CreatePerson);//true
  alert(person2 instanceof CreatePerson);//true
  alert(person2 instanceof Object);//true  使用instanceof來檢測對象的類型

構造函數沒有返回值,沒有顯示地建立對象。
使用new調用構造函數,將構造函數的做用域賦給新對象(this指向新對象)
構造函數模式的特色:使用構造函數建立的對象(實例)有特定的類型。這也是構造函數模式賽過工廠模式的地方

構造函數模式的問題:構造函數中方法,在不一樣的實例中是不一樣的funciton實例,也就是說不一樣實例中有不一樣function(雖然他們的功能相同),
可是這並非咱們但願看到的,更況且function中還有this(指向實例的做用域),因此徹底沒有必要把功能相同function綁定到特定的實例上。
由此產生了原型模式!
大boss來了,接着說下去繼承

原型模式

關於原型的定義,摘自紅寶書:每一個函數都有一個prototype(原型)屬性,這個屬性是一個指針,指向一個對象,而這個對象的用途是包含能夠由特定類型的全部實例共享的屬性和方法。
好長一句話!有點暈?直接上代碼:接口

function Person(){
  }
Person.prototype.name="mike";
Person.prototype.age=12;
Person.prototype.job="Software Engneer";
Person.prototype.sayName=function(){
      alert(this.name);
}

var person1=new Person();
alert(Person1.prototype.isPrototypeOf(person5));//true檢驗實例與原型之間的關係
person1.sayName();//mike
var person2=new Person();
alert(person1.sayName()==person2.sayName());//true


var person6=new Person();
person6.name="ssss";
alert(person6.hasOwnProperty("ssss"));//true

原型鏈查找屬性或方法:

首先查找實例屬性:即定義在子類構造函數中或直接添加在實例上的屬性
而後查找子類原型屬性:包括子類自有的原型屬性和,父類的實例屬性(子類的原型是父類的實例)(這也就是原型鏈繼承的缺點,父類的實例屬性全變成子類的原型屬性,那就是全部子類實例共享的,若是又是引用類型,就出大事了。因此不多單獨使用原型鏈)
最後查找父類的原型屬性ci

function SuperType(){
      this.name="super";
      this.job="student";
}
SuperType.prototype.getSuperValue=function(){
      alert(this.name);
}
SuperType.prototype.sex="man";
function SubType(){
      this.age=12;
}
SubType.prototype=new SuperType();
SubType.prototype.getSubValue=function(){
      alert(this.age);
}
//var person=new SubType();
var instance1=new SubType
alert(instance1 instanceof SubType);//true
alert(instance1 instanceof SuperType);//true
alert(SubType.prototype.isPrototypeOf(instance1));//true
alert(SuperType.prototype.isPrototypeOf(instance1));//true  
person.getSuperValue();
alert(person.constructor==SubType);//false子類的實例的原型對象沒有constructor屬性
alert(person instanceof SubType);
alert(person instanceof SuperType);
alert(person.job);
alert(person.constructor);//sub的原型指向super的原型,super的原型的constuctor是superType,因此person的
alert(person.hasOwnProperty("name"));//false
alert("name" in person);//true 說明name是sub的原型屬性。
alert(person.hasOwnProperty("sex"));//false
alert("sex" in person);//true 說明name是sub的原型屬性。,父類中的所有屬性都是子類的原型屬性
var person1=new SuperType();;
alert(person1.constructor);//

解決原型鏈缺點的方法:借用構造函數原型鏈

借用構造函數

function SuperType(name){
  this.colors=["red","yellow","green"];
  this.name=name;
}
function SubType(){
      SuperType.call(this,"Mike");
      this.age=10;
}
var instance1=new SubType();
instance1.colors.push("aa");
alert(instance1.colors);
alert(instance1 instanceof SubType);//true
alert(instance1 instanceof SuperType);//false
alert(SubType.prototype.isPrototypeOf(instance1));//true
alert(SuperType.prototype.isPrototypeOf(instance1));//false
var instance2=new SubType();
alert(instance2.colors);//["red","yellow","green"];

在新實例的環境中調用Super構造函數

var instance3=new SubType();
alert(instance3.hasOwnProperty("name"));
alert(instance3.hasOwnProperty("age"));

在子類中call(借調)父類的構造函數實際上就是爲子類添加了實例屬性(原來在父類中的實例屬性)
即父類的實例屬性變成了子類的實例屬性(原型鏈繼承中,父類的實例屬性變成了子類的原型屬性)

組合繼承

function SuperType(name){
  this.colors=["red","yellow","green"];
  this.name=name;
}
SuperType.prototype.sayName=function(){
  alert(this.name);
}

function SubType(name,age){
     SuperType.call(this,"Kiko");
      this.age=age;
}
SubType.prototype=new SuperType();
SubType.prototype.constructor=SubType;
SubType.prototype.sayAge=function(){
      alert(this.age);
}
var instance1=new SubType("Kiko",12);
//instance1.sayName();
//instance1.colors.push("ss");
//alert("colors" in instance1);//有屬性
//alert(instance1.hasOwnProperty("colors"))
//實例屬性 沒有這句話的時候//SuperType.call(this,"Kiko");,,是原型屬性
//有了這句話變成了實例屬性
//alert(instance1.colors);
//instance1.sayAge();
alert(instance1 instanceof SubType);//true
alert(instance1 instanceof SuperType);//true
alert(SubType.prototype.isPrototypeOf(instance1));//true
alert(SuperType.prototype.isPrototypeOf(instance1));//true
alert(Object.keys(SubType.prototype));//colors,name,constructor,sayAge
alert(Object.keys(instance1));//colors,name,age

這兩句說明第一:SubType.prototype=new SuperType();父類的全部屬性都變成了子類的原型屬性
第二:SuperType.call(this,"Kiko");,在子類的構造函數中重寫了父類的實例屬性,即父類的實例屬性變成了子類的實例屬性
第三:p149:每當代碼讀取對象中的某個屬性時,都會執行一次搜索,先查找實例屬性,再查找原型屬性
第四:所以,組合繼承中父類中的實例屬性既是子類的實例屬性也是子類的原型屬性,可是在每次使用對象時,查找到實例屬性就截止了,
因此表現爲父類中的實例屬性變成了子類中的實例屬性(實例屬性覆蓋了原型對象上的同名屬性)

var instance2=new SubType("hihi",13);
//alert(instance2.colors);

原型式繼承

function object(o){
     function F(){}
      F.prototype=o;
      return new F();
}

var person={
      name:"Mike",
      friends:["kiko","Court","Van"]
};
var anotherPerson=object(person);
alert(anotherPerson.name);//
anotherPerson.friends.push("ss");

var yetAnotherPerson=object(person);
alert(yetAnotherPerson.friends);//["kiko","Court","Van","ss"]

寄生式繼承

function createAnother(original){
      var clone=object(original);
      clone.sayHi=function(){
    alert("hi");
  }
  return clone;
}
var anotherPerson1=createAnother(person);
anotherPerson.sayHi();//hi

使用寄生式繼承來爲對象添加函數,會因爲不能複用而下降效率,這一點與構造函數模式很相似(同名的方法是不一樣的function實例)

寄生組合式繼承

組合式的缺點是兩次調用超類的構造函數:
一,SubType.prototype=new SuperType();父類的全部屬性都變成了子類的原型屬性
二:SuperType.call(this,"Kiko");,在子類的構造函數中重寫了父類的實例屬性,即父類的實例屬性變成了子類的實例屬性
寄生組合式目的是爲了省掉第一步多繼承的實例屬性,在第一步時只繼承父類的原型屬性,第二步繼承實例屬性

function object(o){
      function F(){}
      F.prototype=o;
      return new F();
}

function inheritPrototype(SubType,SuperType){
      var prototype=object(SuperType.prototype);
      prototype.constructor=SubType;
      SubType.prototype=prototype;
}
function SuperType(name){
      this.name=name;
      this.colors=["red","blue","yellow"];
}
SuperType.prototype.sayName=function(){
      alert(this.name);
}
SuperType.prototype.job="student";

function SubType(name,age){
    SuperType.call(this,name);//(2)繼承實例屬性
      this.age=age;
}
inheritPrototype(SubType,SuperType);//(3)繼承原型屬性
SubType.prototype=new SuperType();//(1)把父類的原型屬性和實例屬性都繼承爲原型屬性了
SubType.prototype.sayAge=function(){
      alert(this.age);
}
var instance1=new SubType();
alert("name" in instance1);//有屬性true
alert(instance1.hasOwnProperty("name"));//false不是實例屬性

這兩句話檢驗是不是原型屬性,1true,2false,原型屬性,1ture,2true實例屬性寄生組合式,name會是實例屬性組合式,把(3)換成(1)name確實也是實例屬性,那是由於(2)從新調用了SuperType,覆蓋了原型中的同名屬性。能夠把(2)去掉,發現name變成了原型屬性。

相關文章
相關標籤/搜索