這篇文章的的目的試圖經過最簡單的表述,讓你們理解
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
繼續上面的線程,咱們按照代碼的執行順序來講明這件事情:
首先咱們定義了兩個函數Car
和MiniCar
,以下圖所示:
而後咱們給Car的原型上添加了一個方法introduce
,以下圖所示:
接下來var car = new Car('porsche')
這一行代碼可不像它看起來那樣,它內部的實現仍是有許多值得玩味的;首先,函數Car
建立了一個新的對象(a),這個對象有一個隱藏的屬性__proto__
,這個屬性和Car
的原型都指向同一個對象.而後Car
函數內部的this
指向哪一個新建立的對象(a).以下圖所示:
而後咱們給這個對象添加了一個屬性name
,而且爲其賦值.還要注意的一點是,咱們這個Car
函數是有返回值的,雖然沒有使用return
關鍵字把這個值顯式的返回,這個返回值是一個引用,而後變量car
就能夠用來操做那個對象了(a).
而後上面的語句運行完以後,場面上是下圖這個樣子:
接下來咱們輸出了這個對象的名字,而後調用了這個對象(的構造函數的原型上的)的introduce
方法.輸出的結果以下圖:
而後咱們有定義了一個函數MiniCar
,咱們把它當作Car
(父類)的一個子類
;我使用代碼MiniCar.prototype = new Car()
來實現這個功能,這段代碼更值得好好分析一下.
首先如上圖所示,MiniCar
這個函數的prototype
是函數Car
使用new
關鍵字建立的一個對象(b),因此MiniCar
的實例具備這個對象(b)可以使用的任何屬性和方法.
讓咱們更進一步吧,這一步咱們開始運行var miniCar = new MiniCar('benz', 'black')
這段代碼,首先咱們先要運行函數MiniCar
函數,因此經過new
操做,咱們新建立了一個對象(c),咱們首先給這個對象添加了了兩個屬性,分別是name
和color
,而後分別賦值benz
和black
,其實咱們能夠只添加一個屬性,由於name
屬性在Car
上是已經存在的.咱們還給它添加了一個getColor
方法,它的__proto__
屬性指向MiniCar.prototype
, 而MiniCar.prototype
是一個對象,這個對象也有一個__proto__
屬性,這個屬性指向Car.prototype
,如此一來這個僞繼承就實現了.而後咱們將這個對象(c)的索引賦值給miniCar
,因此經過miniCar
能夠操做對象(c).
而後接下來的一切應該都瓜熟蒂落了.
原文的地址github
參考的文章或者問答: