## **原型**
歸根結底,原型其實也是一個對象,對象也是 `ECMAScript` 中最基本的概念。
那么我們來看一下對象這個概念,舉一個基本 `Object` 的例子,首先我們要清楚,一個 `Object` 的原型是一個內部的 `[[prototype]]` 屬性的引用。
不過我們一般會使用 `__proto__` 來來代替 `[[prototype]]`,不過這只是某些瀏覽器給出的便捷訪問的方式,所以并非標準,但不妨礙我們了解原型。
*` Object`*
```
var foo = {
x: 10,
y: 20
}
foo.__proto__ === Object.prototype // true
```
我們可以看到 `foo` 是一個普通對象,谷歌可以通過 `__proto__` 來訪問對象原型。
## **原型鏈**
原型對象是一個對象,并且有可能有自己的原型,如果一個原型對象的原型不是 `null`,就有可能還有自己的原型,這就像鏈條一樣,構成了原型鏈。
> A prototype chain is a finite chain of objects which is used to implemented inheritance and shared properties.
原型鏈是一個由對象組成的有限對象鏈由于實現繼承和共享屬性。
想象一個這種情況,2個對象,大部分內容都一樣,只有一小部分不一樣,很明顯,在一個好的設計模式中,我們會需要重用那部分相同的,而不是在每個對象中重復定義那些相同的方法或者屬性。在基于類 `[class-based]` 的系統中,這些重用部分被稱為類的繼承 – 相同的部分放入 `class A`,然后 `class B` 和 `class C` 從A繼承,并且可以聲明擁有各自的獨特的東西。
`ECMAScript` 沒有類的概念。但是,重用 `[reuse]` 這個理念沒什么不同(某些方面,甚至比 `[class-based]` 更加靈活),可以由 `prototype chain` 原型鏈來實現。這種繼承被叫做原型繼承。
```
var a = {
x: 10,
calculate: function (z) {
return this.x + this.y + z
}
}
var b = {
y: 20,
__proto__: a
}
var c = {
y: 30,
__proto__: a
}
// 上面對象表達式中直接聲明了__proto__屬性為a, 所以可以使用原型a上面的方法
b.calculate(30) // 10 + 20 + 30 = 60
c.calculate(40) // 10 + 30 + 40 = 80
```
這種繼承方式很直觀,`b` 和 `c` 都可以訪問他們的 `__proto__` 原型對象里面的屬性。
所以原型鏈有下列關系:
```
// 函數對象實例, 例如:
var Foo = function () {}
Foo.__proto__ === Function.prototype
// 普通對象實例, 例如:
var object = {}
object.__proto__ === Object.prototype
// 因為所有的原型鏈盡頭都是Object.prototype -> null
// 所以Function.prototype 也有自己的原型
Function.prototype.__proto__ === Object.prototype
Object.prototype.__proto__ === null
// 所以總結為:
Object.__proto__ === Function.prototype
Function.prototype.__proto__ === Object.prototype
Object.prototype.__proto__ === null
```
由上可知:***函數對象實例***比 ***普通對象實例*** 要多一層原型鏈,而且只有 ***函數*** 才有 `prototype` 屬性,與之相對應的實例屬性是 `__proto__`,這就是默認的對象原型和原型鏈。
## **構造函數**
但是通常我們不會用上面那個方式實現繼承,我們會封裝構造函數來創建新的實例:
```
// 構造函數
function Foo(y) {
// 構造函數將會以特定模式創建對象:被創建的對象都會有"y"屬性
this.y = y
}
// Foo.prototype 存放了新建對象的原型引用
// 所以這里定義繼承和共享的屬性或方法
Foo.prototype = {
x: 10,
calculate: function (z) {
return this.x + this.y + z
}
}
// this.y = y
var b = new Foo(20)
var c = new Foo(30)
// 調用繼承方法
b.calculate(30) // 10 + 20 + 30 = 60
c.calculate(40) // 10 + 30 + 40 = 80
// 實例的 __proto__ 屬性 === 構造函數的 prototype 屬性
b.__proto__ === Foo.prototype // true
c.__proto__ === Foo.prototype // true
// Foo.prototype 自動創建了一個特殊的屬性 constructor
// 這個屬性等于構造函數本身
b.constructor === Foo // true
c.constructor === Foo // true
Foo.prototype.constructor === Foo // true
// 下面證明實例繼承來的方法和原型上的方法是同一個
b.calculate === b.__proto__.calculate // true
b.__proto__.calculate === Foo.prototype.calculate // true
```

因為 `__proto__` 并非標準,`Foo.prototype` 才是顯式的屬性,也就是 `b` 和 `c` 的 `__proto__` 屬性。
每個對象都有原型,表現出來的形式是構造函數的 `prototype` 屬性,所以我們一般操作原型的時候都會使用構造函數。
可以這么說,所有的對象都有原型,根據不同瀏覽器訪問的方式可能會不同,拿谷歌為例,可以訪問對象的 `__proto__` 屬性來訪問原型,`arguments` 是一個類數組,不是數組卻是對象,所以:
```
function Foo () {
console.log(arguments.__proto__ === Object.prototype) // true
console.log(Object.__proto__ === Function.prototype) // true
}
```