constructor, prototype, __proto__ 詳解

本文爲了解決如下問題:javascript

  • __proto__(實際原型)和prototype(原型屬性)不同!!!java

  • constructor屬性(原型對象中包含這個屬性,實例當中也一樣會繼承這個屬性)函數

  • prototype屬性(constructor.prototype原型對象)this

  • __proto__屬性(實例指向原型對象的指針)spa

首先弄清楚幾個概念:.net

什麼是對象

若干屬性的集合prototype

什麼是原型?

原型是一個對象,其餘對象能夠經過它實現繼承。指針

哪些對象有原型?

全部的對象在默認狀況下都有一個原型,由於原型自己也是對象,因此每一個原型自身又有一個原型(只有一種例外,默認的對象原型在原型鏈的頂端)code

任何一個對象均可以成爲原型

接下來就是最核心的內容:htm

constructor 屬性

constructor屬性始終指向建立當前對象的構造函數。

var arr=[1,2,3];
    console.log(arr.constructor); //輸出 function Array(){}
    var a={};
    console.log(arr.constructor);//輸出 function Object(){}
    var bool=false;
    console.log(bool.constructor);//輸出 function Boolean(){}
    var name="hello";
    console.log(name.constructor);//輸出 function String(){}
    var sayName=function(){}
    console.log(sayName.constrctor)// 輸出 function Function(){}
    
    //接下來經過構造函數建立instance
    function A(){}
    var a=new A();
    console.log(a.constructor); //輸出 function A(){}

以上部分即解釋了任何一個對象都有constructor屬性,指向建立這個對象的構造函數

prototype屬性

注意:prototype是每一個函數對象都具備的屬性,被稱爲原型對象,而__proto__屬性纔是每一個對象纔有的屬性。一旦原型對象被賦予屬性和方法,那麼由相應的構造函數建立的實例會繼承prototype上的屬性和方法

//constructor : A
    //instance : a
    function A(){}
    var a=new A();

    A.prototype.name="xl";
    A.prototype.sayName=function(){
        console.log(this.name);
    }
    
    console.log(a.name);// "xl"
    a.sayName();// "xl"
    
    //那麼由constructor建立的instance會繼承prototype上的屬性和方法

constructor屬性和prototype屬性

每一個函數都有prototype屬性,而這個prototypeconstructor屬性會指向這個函數。

function Person(name){
        this.name=name;
    }
    Person.prototype.sayName=function(){
        console.log(this.name);
    }
    
    var person=new Person("xl");
    
    console.log(person.constructor); //輸出 function Person(){}
    console.log(Person.prototype.constructor);//輸出 function Person(){}
    console.log(Person.constructor); //輸出 function Function(){}

若是咱們重寫(從新定義)這個Person.prototype屬性,那麼constructor屬性的指向就會發生改變了。

Person.prototype={
        sayName:function(){
            console.log(this.name);
        }
    }
    
    console.log(person.constructor==Person); //輸出 false (這裏爲何會輸出false後面會講)
    console.log(Person.constructor==Person); //輸出 false
    
    console.log(Person.prototype.constructor);// 輸出 function Object(){}  
    //這裏爲何會輸出function Object(){}
    //還記得以前說過constructor屬性始終指向建立這個對象的構造函數嗎?
    
    Person.prototype={
        sayName:function(){
            console.log(this.name);
        }
    }
    //這裏其實是對原型對象的重寫:
    Person.prototype=new Object(){
        sayName:function(){
            console.log(this.name);
        }
    }
    //看到了吧。如今Person.prototype.constructor屬性其實是指向Object的。
    
    //那麼我如何能將constructor屬性再次指向Person呢?
    Person.prototype.constructor=Person;

接下來解釋爲何,看下面的例子

function Person(name){
        this.name = name;
    }
    
    var personOne=new Person("xl");
    
    Person.prototype = {
        sayName: function(){
            console.log(this.name);
        }
    };
    
    var personTwo = new Person('XL');
    
    console.log(personOne.constructor == Person); //輸出true
    console.log(personTwo.constructor == Person); //輸出false   
    //你們可能會對這個地方產生疑惑?爲什麼會第二個會輸出false,personTwo不也是由Person建立的嗎?這個地方應該要輸出true啊?
    //這裏就涉及到了JS裏面的原型繼承
    //這個地方是由於person實例繼承了Person.prototype原型對象的全部的方法和屬性,包括constructor屬性。當Person.prototype的constructor發生變化的時候,相應的person實例上的constructor屬性也會發生變化。因此第二個會輸出false;
    //固然第一個是輸出true,由於改變構造函數的prototype屬性是在personOne被建立出來以後。

接下解釋__proto__prototype屬性
一樣拿上面的代碼來解釋:

function Person(name){
        this.name=name;
    }
    Person.prototype.sayName=function(){
        console.log(this.name);
    }
    var person=new Person("xl");
    person.sayName(); //輸出 "xl"

    //constructor : Person
    //instance : person
    //prototype : Person.prototype

首先給構造函數的原型對象Person.prototype賦給sayName方法,由構造函數Person建立的實例person會繼承原型對象上的sayName方法。

爲何會繼承原型對象的方法?

由於ECMAscript的發明者爲了簡化這門語言,同時又保持繼承性,採用了鏈式繼承的方法。

constructor建立的每一個instance都有個__proto__屬性,它指向constructor.prototype。那麼constrcutor.prototype上定義的屬性和方法都會被instance所繼承.

function Person(name){
        this.name=name;
    }
    Person.prototype.sayName=function(){
        console.log(this.name);
    }
    
    var personOne=new Person("a");
    var personTwo=new Person("b");
    
    personOne.sayName(); // 輸出  "a"
    personTwo.sayName(); //輸出 "b"
    
    console.log(personOne.__proto__==Person.prototype); // true
    console.log(personTwo.__proto__==Person.prototype); // true
    
    console.log(personOne.constructor==Person); //true
    console.log(personTwo.constructor==Person); //true
    console.log(Person.prototype.constructor==Person); //true
    
    console.log(Person.constructor); //function Function(){}
    console.log(Person.__proto__.__proto__); // Object{}

圖片描述

參考文章:

  1. 強大的原型和原型鏈

  2. js原型鏈看圖說明

  3. 理解javascript原型

  4. javascript類和繼承:constructor

  5. javascript探祕:構造函數

相關文章
相關標籤/搜索