因為這個概念的了解,總是讓我繞進去,有點懷疑智商……
今天好好了解一下這個概念!
先看一個例子:
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_
屬性。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIwczLcVmds92czlGZvwVP9EUTDZ0aRJkSwk0LcxGbpZ2LcBDM08CXlpXazRnbvZ2LcRlMMVDT2EWNvwFdu9mZvwVP9cXT1sGVNVnRXRWc1clWv5kMMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2LcRHelR3LcJzLctmch1mclRXY39DNyETOykjMyIzMygDM4EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
其實,隻需要明白原型對象的結構即可:
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>