理解JavaScript的prototype和__proto__

這篇文章的的目的試圖經過最簡單的表述,讓你們理解prototype__proto__javascript

先把最重要的幾點列出來,你們能夠帶着這幾個核心要點閱讀下面的文章.html

  • __proto__是用來在原型鏈上查找你須要的方法的實際對象,全部的對象都有這個屬性.這個屬性被JavaScript引擎用做繼承使用.
    根據ECMA的規範,這個屬性應該是一個內在的屬性,可是大多數的瀏覽器廠商都容許咱們去訪問和修改它.java

  • prototype是函數獨有的屬性.當咱們使用關鍵詞new而且將函數做爲構造函數來構造對象的時候,
    它被用來構建對象的__proto__屬性.python

  • __proto__屬性和prototype屬性都是一個對象代碼演示.git

  • (new A()).__proto__ === A.prototype的結果爲true,(new A()).prototype === undefined的結果也爲true,其中A表示一個函數(也就是構造函數).github

接下來咱們來使用一些代碼來解釋上面所說的那些要點:代碼演示web

// 這是一個普通函數,咱們把它用來當作構造函數,也當作一個[父類]
    function Car(name) {
        this.name = name;
    }
    Car.prototype.introduce = function() {
      console.log('[From Car.prototype.introduce] ' + 'Hello, my name is: ' + this.name);
    };
    
    var car = new Car('porsche');
    console.log(car.name); // porsche
    car.introduce(); // [From Car.prototype.introduce] Hello, my name is: porsche
    
    
    // 咱們開始構建另一個函數,咱們把這個函數當作一個[子類],暫時這麼說.
    function MiniCar(name, color) {
        this.name = name;
        this.color = color;
    
        this.getColor = function() {
            console.log('My color is: ' + this.color);
        }
    }
    MiniCar.prototype = new Car();
    
    var miniCar = new MiniCar('benz', 'black');
    console.log('\n');
    console.log('name: ' + miniCar.name + ';color: ' + miniCar.color); // name: benz;color: black
    miniCar.introduce(); // [From Car.prototype.introduce] Hello, my name is: benz
    miniCar.getColor(); // My color is: black
    
    // 若是使用A表示一個構造函數,那麼 (new A()).__proto__ === A.prototype
    console.log((new MiniCar()).__proto__ === MiniCar.prototype); // true
    
    // 若是使用a表示A的一個示例的話,那麼 a.__proto__ === A.prototype
    console.log(miniCar.__proto__ === MiniCar.prototype); // true
    
    // 一個對象是沒有prototype屬性的
    console.log(miniCar.prototype === undefined); // true

若是你練習了上面的代碼,對這兩個屬性的理解應該會有必定的幫助,也許你已經理解了;若是沒有太懂的話,那也不要緊;咱們下面來好好的說一說上面的代碼(開始長篇大論了).
首先,在JavaScript中是沒有這個概念的,若是你學過Java或者C++的話,應該知道,要是想建立一個對象,必須先有一個;可是JavaScript中沒有,那怎麼辦?模仿嘍,因此JavaScript創造了__proto__這個屬性用來鏈接子類父類.創造了prototype屬性去用來在構建子類時候構建__proto__這個屬性.編程

這裏先暫停上面的線程,咱們來講說prototype這個屬性,這個屬性是隻屬於Function函數的,那麼這個屬性的做用是什麼呢?這個屬性的做用是爲了讓使用Function做爲構造函數new出來的對象實例都可以共享一些函數.瀏覽器

function Car(name) {
           this.name = name;
       }
   Car.prototype.introduce = function() {
     console.log('[From Car.prototype.introduce] ' + 'Hello, my name is: ' + this.name);
   };

上面的代碼中,只要是使用Car這個構造函數new出來的對象都具備方法introduce.app

繼續上面的線程,咱們按照代碼的執行順序來講明這件事情:

  • 首先咱們定義了兩個函數CarMiniCar,以下圖所示:
    1

  • 而後咱們給Car的原型上添加了一個方法introduce,以下圖所示:
    2

  • 接下來var car = new Car('porsche')這一行代碼可不像它看起來那樣,它內部的實現仍是有許多值得玩味的;首先,函數Car建立了一個新的對象(a),這個對象有一個隱藏的屬性__proto__,這個屬性和Car的原型都指向同一個對象.而後Car函數內部的this指向哪一個新建立的對象(a).以下圖所示:
    3

  • 而後咱們給這個對象添加了一個屬性name,而且爲其賦值.還要注意的一點是,咱們這個Car函數是有返回值的,雖然沒有使用return關鍵字把這個值顯式的返回,這個返回值是一個引用,而後變量car就能夠用來操做那個對象了(a).
    4

  • 而後上面的語句運行完以後,場面上是下圖這個樣子:
    5

  • 接下來咱們輸出了這個對象的名字,而後調用了這個對象(的構造函數的原型上的)的introduce方法.輸出的結果以下圖:
    6

  • 而後咱們有定義了一個函數MiniCar,咱們把它當作Car(父類)的一個子類;我使用代碼MiniCar.prototype = new Car()來實現這個功能,這段代碼更值得好好分析一下.
    7首先如上圖所示,MiniCar這個函數的prototype是函數Car使用new關鍵字建立的一個對象(b),因此MiniCar的實例具備這個對象(b)可以使用的任何屬性和方法.

  • 讓咱們更進一步吧,這一步咱們開始運行var miniCar = new MiniCar('benz', 'black')這段代碼,首先咱們先要運行函數MiniCar函數,因此經過new操做,咱們新建立了一個對象(c),咱們首先給這個對象添加了了兩個屬性,分別是namecolor,而後分別賦值benzblack,其實咱們能夠只添加一個屬性,由於name屬性在Car上是已經存在的.咱們還給它添加了一個getColor方法,它的__proto__屬性指向MiniCar.prototype, 而MiniCar.prototype是一個對象,這個對象也有一個__proto__屬性,這個屬性指向Car.prototype,如此一來這個僞繼承就實現了.而後咱們將這個對象(c)的索引賦值給miniCar,因此經過miniCar能夠操做對象(c).
    8

  • 而後接下來的一切應該都瓜熟蒂落了.
    9

原文的地址github


參考的文章或者問答:

相關文章
相關標籤/搜索