我們創建的每一個函數都有一個prototype(原型)屬性,這個屬性是一個對象,它的用途是包含可以由特定類型的所有實例共享的屬性和方法。邏輯上可以這么理解:prototype通過調用構造函數而創建的那個對象的原型對象。使用原型的好處可以讓所有對象實例共享它所包含的屬性和方法。也就是說,不必在構造構造函數中定義對象信息,而是可以直接將這些信息添加到原型中。
```
function Box () {}
Box.prototype.name = 'Lee'
Box.prototype.age = 100
Box.prototype.run = function () {
return this.name + this.age
}
```
比較一下原型內的方法地址是否一致:
```
var box1 = new Box()
var box2 = new Box()
box1.run === box2.run // true
box1.name === box2.name // true
box1.name = 'Jack'
box2.name // 'Jack'
```
為了更進一步了解構造函數的聲明方式和原型模式的聲明方式,我們通過圖示來了解一下:


***
構造函數上有一個prototype屬性,這個屬性是一個對象
構造函數產生的實例上有一個__proto__屬性,這個屬性指向prototype屬性
***
在原型模式聲明中,多了兩個屬性,這兩個屬性都是創建對象時自動生成的。__proto__屬性是實例指向原型對象的一個指針,它的作用就是指向構造函數的原型屬性constructor。通過這兩個屬性,就可以訪問到原型里的屬性和方法了。
PS:IE瀏覽器在腳本訪問__proto__會不能識別,火狐和谷歌瀏覽器及其他某些瀏覽器均能識別。雖然可以輸出,但無法獲取內部信息。
## isPrototypeOf
判斷一個對象是否指向了該構造函數的原型對象,可以使用isPrototypeOf()方法來測試。
Box.prototype.isPrototypeOf(box)
原型模式的執行流程:
1. 先查找構造函數實例的屬性或方法,如果有,立刻返回;
2. 如果構造函數實例里沒有,則去它的原型對象里找,如果有,就返回
## hasOwnProperty
判斷一個屬性是在構造函數的實例里,還是在原型里?可以使用hasOwnProperty()函數來驗證:
box.hasOwnProperty('name') // 實例里面有返回true,否則返回false
## in
in操作符會在通過對象能夠訪問給定屬性時返回true,無論該屬性存在于實例中還是原型中
'name' in box // true,存在實例中或原型中
## 判斷屬性是否只存在于原型中
```
function isProperty (object, property) {
return !object.hasOwnProperty(property) && (property in object)
}
```
## 字面量方式
```
function Box () {}
Box.prototype = {
name: 'Lee',
age: 100,
run: function () {
return this.name + this.age
}
}
```
使用構造函數創建原型對象和使用字面量創建對象在使用上基本形同,但還是有一些區別,字面量創建的方式使用constructor屬性不會指向實例,而是指向了Object,構造函數創建的方式責罰相反
```
var box = new Box()
box instanceof Box // true
box instanceof Object //
box.constructor === Box
box.constructor === Object
```
強制指向Box
```
Box.prototype = {
constructor: Box
}
```
## 構造函數和原型搭配
原型模式創建對象也有自己的缺點,它省略了構造函數傳參初始化這一過程,帶來的缺點就是初始化的值都是一樣的,而原型最大的缺點就是它最大的優點,那就是共享。
原型中所有屬性是被很多實例共享的,共享對于函數非常合適,對于包含基本值的屬性也還可以,但如果屬性包含引用類型,就存在一定的問題:某個實例修改了引用屬性,則其他實例的引用屬性也會被修改。
```
function Box (name ,age) {
this.name = name
this.age = age
this.family = ['父親', '母親', '妹妹']
}
Box.prototype = {
constructor: Box,
run: function () {
return this.name + this.age + this.family
}
}
```
這種混合模式很好的解決了傳參和引用共享的兩大難題,是創建對象比較好的方法。
## 動態原型模式
原型模式,不管你是否調用了原型中的共享方法,它都會初始化原型中的方法,并且在聲明一個對象時,構造函數+原型模式讓人感覺很怪異,最好的就是把構造函數和原型封裝到一起,動態原型模式可以解決這個問題
```
function Box (name ,age) {
this.name = name
this.age = age
// 原型只需要初始化一次
if (typeof this.run === 'undefined') {
Box.prototype.run = function () {
return this.name + this.age
}
}
}
```