理解javascript中的prototype
以前一直對javascript中的prototype不是很理解,今天在閱讀了《javascript進階程式設計之後》終於理解了其中的prototype。來簡單的總結一下從書中學習到的內容。
我們都知道在建立了一個function之後,這個function就具有了prototype這樣的一個屬性,利用這個prototype我們可以做很多的事情,其中我們經常用到的一點就是利用它來當做建構函式,因此,本文重要從function作為建構函式的角度來說明一下prototype。
其實javascript中的建構函式與其他的函數沒有什麼區別,只是調用的方式的略微點不一樣。下面來看代碼。
function Dog(){ this.name = Ben; }我們聲明了一個簡單的函數,叫做Dog,如果我們只是簡單的調用這個方法 ,那麼她只會為全域對象添加一個name屬性。並不會產生對象。
但是當我們把這個方法當做建構函式來調用的時候,情況就會不一樣了,這個函數會返回一個對象。
var d = Dog();console.log(d) //undefined //作為建構函式調用 var dog = new Dog(); console.log(dog) // dog對象。
這屬於javascript中產生對象的方式了,相信大家應該都明白,下面我們就來詳細說明這個prototype在這個建構函式中到底起了什麼作用。
在文章開頭的時候,就已經說了,在聲明建構函式的時候,這個函數已經prototype這樣一個屬性。在chrome的控制台中,輸入代碼可以查看到這樣的一個結果。
console.log(Dog.prototype); //Dog {} 實際上函數的prototype指向了一個對象,稱作為原型對象,這個對象其中包含了一個屬性叫做constructor,但是為什麼我們在輸出的對象之中看不到它呢,這是在javascript中,把這個屬性的Erumerable屬性設定為false了,所以在這裡我們看不到,但是看不到我們還是可以訪問到這個屬性。看下面的結果:
console.log(Dog.prototype.constructor); // function Dog(){ // this.name = Ben; // } 看到結果,是不是發現了什麼,輸出來的結構就是我們自己定義的建構函式,那麼他們到底是什麼關係呢?我們可以使用相等操作符來比較一下。
console.log(Dog.prototype.constructor === Dog);//true
我們可以看到結果,這兩個實際上是完成相等的。
這麼說來,我們是不是又可以用constructor來訪問到prototype呢?沒錯,這樣是完全可以的,這樣一樣,這裡面就相當於形成了一個迴圈指向關係,這個實際上也可以採用遞迴的方式來驗證一下,調用遞迴的時候很快就會調用棧溢出,因為在這個遞迴調用中是沒有出口的。
上面的關係我們可以簡單的用下面的圖來表示一下
好,上面我們已經簡單的介紹了一下建構函式中的prototype,那麼我們再來看一下,對象中的prototype,在javascript中,聲明對象中並沒有prototype這個屬性,而是使用__proto__(注意:__是兩個_) 來訪問prototype ,那麼對象中的__proto__跟建構函式中的prototype又是什麼關係呢?
dog.__proto__ === Dog.prototype;//true
結果一看便知,通過這個結果我們就可以知道同一個類的所有對象的__proto__都是相等的,因為他們實際上都指向了建構函式的prototype。
現在看完了,建構函式和對象的的prototype,我們再來看一下prototype這個對象本身,剛才我們已經提過了,prototype其實本身就是一個對象,既然他是一個對象,我們就可以擴充這個對象,下面我們為這個對象添加一個方法和屬性。
Dog.prototype.say = function(){console.log('wangwang');}Dog.prototype.color = black;
我們再來看一下剛才自己定義的那個dog對象,是不是也有了這個方法和屬性。
dog.day() ;// 'wangwang'dog.color ;// 'black';
我們通過調用發現,這個這個對象上也有了個方法和屬性,但是我們並沒有直接在對象添加方法和屬性,這就說明了,prototype中的屬性和方法也會為對象共用。
在這裡共用是通過尋找得到的,當我們訪問一個對象的屬性時,這個對象首先會在自己的屬性集裡面去找這個屬性,如果找到了,那麼就直接返回,如果沒有找到就會去prototype的屬性集裡面找。在這個例子中我們當我們訪問屬性和方法的時候,首先在dog對象裡面找,找不到之後再去prototype的屬性和方法找。
既然這些方法和屬性都是共用的,那麼我們修改這些方法和屬性會有什麼結果呢?
var dog2 = new Dog(); dog.color = red; console.log(dog2.color); //black
從結果可以看到,dog2的color屬性並沒有受到影響,這是因為在使用對象去操作prototype的屬性時,並沒有真正的操作prototype,而是自己建立了一個color屬性,並且將red賦值給了color,所以這裡並沒有影響到prototype。也就沒有影響到dog2了。(讀者可以自己驗證一下,color是作為dog對象的屬性而存在的)。
同樣的我們可以看到當我們修改了方法之後,prototype中方法並沒有收到影響。
關於prototype的簡單說明就到這裡。文中如有錯位還望各位大神指正。