一、了解原型對象
當建立一個新函數時,系統會根據一組特定的規則為函數建立一個prototype屬性,該屬性會指向一個名為原型對象的對象,在預設情況下,該對象會自動生成一個構造函數(constructor),該構造函數是一個指向函數的指針。而在原型對象中,除了有這個構造函數,我們還可以添加其他的屬性和方法。
通俗來講就是,當我們建立一個函數A時,函數A内部會有一個屬性,該屬性指向一個對象(名字叫原型對象),而這個對象裡面預設有一個構造函數,這個構造函數指向我們最初建立的函數A。然後,我們還可以在原型對象中添加屬性和方法。
下面我們來看構造函數、構造函數的原型屬性和執行個體之間的關系:
構造函數内部會有一個prototype屬性,該屬性指向構造函數的原型對象,當通過構造函數建立執行個體時,該執行個體内部将包含一個指針,指向構造函數的原型屬性。該關系如圖所示
來看下面的例子:
//①預設情況下,構造函數是空的
function Person(){//構造函數首字母大寫
}
//②添加屬性和方法
Person.prototype.name="dp";
Person.prototype.doSomething=function(){
alert(this.name);
};
//③定義好構造函數和其他屬性方法之後,就可以建立執行個體了
var person1=new Person();
var person2=new Person();
這兩個對象都是由A建立的,他們都可以調用A的原型中的屬性和方法
alert(person1.name);//dp
alert(person2.name);//dp
當然也可以将其重寫
person1="god";
alert(person1.name);//god
重寫後的屬性也可以删除
delete person1.name;
alert(person1.name);//dp
注意原型的動态性,如果你将①和②調換位置,如下:
function Person(){ }
var person3=new Person();
Person.prototype.name=”dp”;
然後你再執行alert(person3.name)會報錯,因為new完person3之後,相當于person3的構造函數和原型屬性都是預設值(空),而接下來的Person.prototype.name=”dp”相當于重寫了其原型對象,重寫原型對象的同時,割裂了原型對象和執行個體的關系,既然原型對象和執行個體沒有關系了,那麼person3.name就沒有意義,是以會報錯。
二、了解原型鍊
讓我們回顧一下構造函數、原型和執行個體的關系:構造函數内部會有原型對象,該原型對象中包含一個指向構造函數的指針,而執行個體包含一個指向原型對象的指針。
那麼,假如原型對象是另一個類型的執行個體呢?也就是說,原型對象包含一個内部指針,指向另一個類型的原型對象,相應的,該原型對象也包含一個指向其構造函數的指針,如圖所示。如果将這種關系延續下去,就形成了原型鍊。
實作原型鍊的基本模式如下:
<script type="text/javascript">
function Animal(name) {
this.name = name;//設定對象屬性
}
Animal.prototype.getName = function() {
alert("It is a "+this.name);
}
function Dog(name){
this.name=name;
}
Dog.prototype=new Animal();//繼承了Animal
Dog.prototype.getName=function(){
alert("It is a "+this.name);
};
var tom = new Dog("dog");//建立Dog對象
alert(tom.getName());//It is a dog
</script>
三、繼承
許多語言都支援兩種繼承方式:接口繼承和實作繼承。在ECMAscript中不支援接口繼承,隻支援實作繼承,而實作繼承主要是依靠原型鍊來實作。根據js語言的本身的特性,js實作繼承有五種方式,詳見部落格