## **原型**
归根结底,原型其实也是一个对象,对象也是 `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
```
![](https://box.kancloud.cn/a504f06150f594c7af52a7ed2eda45ec_627x392.png)
因为 `__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
}
```