天天看點

原型和原型鍊的了解

因為這個概念的了解,總是讓我繞進去,有點懷疑智商……

今天好好了解一下這個概念!

先看一個例子:

function Person(){
    this.name = 'Mike';
}
var person = new Person();
Person.prototype.say = function(){
    console.log('Hello,'+this.name);
};
person.say();  //Hello,Mike
           

上述代碼非常簡單,Person原型對象定義了公共的say方法,雖然此舉在構造執行個體之後出現,但因為原型方法在調用之前已經聲明,是以之後的每個執行個體将都擁有該方法。從這個簡單的例子裡,我們可以得出:

原型對象的用途:是為每個執行個體對象存儲共享的方法和屬性,它僅僅是一個普通的對象而已。并且所有的執行個體是共享同一個原型對象,是以有差別于執行個體方法或屬性,原型對象僅有一份。

是以就會有如下等式成立:

person.say == new Person().say

亦或者我們也可以這樣寫

function Person() {
    this.name = 'Mike';
}
var person = new Person();
Person.prototype = {
    say: function() {
       console.log('Hello,' + this.name);
    }
};
person.say();//person.say is not a function
           

很不幸,person.say方法沒有找到,是以報錯了。其實這樣寫的初衷是好的:因為如果想在原型對象上添加更多的屬性和方法,我們不得不每次都要寫一行Person.prototype,還不如提煉成一個Object來的直接。但是此例子巧就巧在構造執行個體對象操作是在添加原型方法之前,這樣就會造成一個問題:

var person = new Person()

時,Person.prototype為:Person {}(當然了,内部還有constructor屬性),即

Person.prototype

指向一個空的對象{}。而對于執行個體person而言,其内部有一個原型鍊指針

__proto__

,該指針指向了Person.prototype指向的對象,即{}。接下來重置了Person的原型對象,使其指向了另外一個對象,即

Object {say: function}
           

這時person.proto的指向還是沒有變,它指向的{}對象裡面是沒有say方法的,因為報錯。 從這個現象我們可以得出:

在js中,對象在調用一個方法時會首先在自身裡尋找是否有該方法,若沒有,則去原型鍊上去尋找,依次層層遞進,這裡的原型鍊就是執行個體對象的

_proto_

屬性。

原型和原型鍊的了解

其實,隻需要明白原型對象的結構即可:

Function.prototype = {
        constructor : Function,
        __proto__ : parent prototype,
        some prototype properties: ...
    };
           

總結:函數的原型對象constructor預設指向函數本身,原型對象除了有原型屬性外,為了實作繼承,還有一個原型鍊指針proto,該指針指向上一層的原型對象,而上一層的原型對象的結構依然類似,這樣利用proto一直指向Object的原型對象上,而Object的原型對象用Object.prototype.proto = null表示原型鍊的最頂端,如此變形成了javascript的原型鍊繼承,同時也解釋了為什麼所有的javascript對象都具有Object的基本方法。

與原型有關的幾個方法和屬性總結:

  • prototype屬性

    prototype 存在于構造函數中 (其實任意函數中都有,隻是不是構造函數的時候prototype我們不關注而已) ,他指向了這個構造函數的原型對象。

  • constructor屬性

    constructor屬性存在于原型對象中,他指向了構造函數

<script type="text/javascript">
    function Person () {
    }
    alert(Person.prototype.constructor === Person); // true
    var p1 = new Person();
    //使用instanceof 操作符可以判斷一個對象的類型。  
    //typeof一般用來擷取簡單類型和函數。而引用類型一般使用instanceof,因為引用類型用typeof 總是傳回object。
    alert(p1 instanceof Person);    // true
</script>
           
  • _proto_ 屬性(注意:左右各是2個下劃線)

    用構造方法建立一個新的對象之後,這個對象中預設會有一個不可通路的屬性 [

    [prototype]

    ] , 這個屬性就指向了構造方法的原型對象。

​ 但是在個别浏覽器中,也提供了對這個屬性[[prototype]]的通路(chrome浏覽器和火狐浏覽器。ie浏覽器不支援)。通路方式:

p1.__proto__

  • hasOwnProperty() 方法

    大家知道,我們用去通路一個對象的屬性的時候,這個屬性既有可能來自對象本身,也有可能來自這個對象的[[prototype]]屬性指向的原型。

​ 那麼如何判斷這個對象的來源呢?

hasOwnProperty

方法,可以判斷一個屬性是否來自對象本身。

<script type="text/javascript">
    function Person () {

    }
    Person.prototype.name = "志玲";
    var p1 = new Person();
    p1.sex = "女";
    //sex屬性是直接在p1屬性中添加,是以是true
    alert("sex屬性是對象本身的:" + p1.hasOwnProperty("sex"));
    // name屬性是在原型中添加的,是以是false
    alert("name屬性是對象本身的:" + p1.hasOwnProperty("name"));
    //  age 屬性不存在,是以也是false
    alert("age屬性是存在于對象本身:" + p1.hasOwnProperty("age"));

</script>
           

注意:通過hasOwnProperty這個方法可以判斷一個對象是否在對象本身添加的,但是不能判斷是否存在于原型中,因為有可能這個屬性不存在。

也即是說,在原型中的屬性和不存在的屬性都會傳回fasle。

  • in 操作符

​ in操作符用來判斷一個屬性是否存在于這個對象中。但是在查找這個屬性時候,現在對象本身中找,如果對象找不到再去原型中找。換句話說,隻要對象和原型中有一個地方存在這個屬性,就傳回true

<script type="text/javascript">
    function Person () {

    }
    Person.prototype.name = "志玲";
    var p1 = new Person();
    p1.sex = "女";
    alert("sex" in p1);     // 對象本身添加的,是以true
    alert("name" in p1);    //原型中存在,是以true
    alert("age" in p1);     //對象和原型中都不存在,是以false

</script>
           

繼續閱讀