[TOC=3,3]
* * * * *
### 简述
每一个Javascript对象(null除外)都和另一个对象相关联。“另一个”对象就是我们熟知的原型,每一个对象都从原型继承属性。
所有通过对象直接量创建的对象都具有一个原型对象,并可以通过javascript代码`object.prototype`获得对原型对象的引用。通过关键字new和构造函数调用创建的对象的原型就是构造函数的`prototype`属性的值。因此,同使用`{}`创建对象一样,通过`new object()`创建的对象也继承自`object.prototype`。同样,通过`new Array()`创建的对象的原型也是`Array.prototype`,通过`new Date()`创建的对象的原型就是`Date.prototype`。
### 细说
**prototype 是通过调用构造函数而创建的对象实例的原型对象。**
* 每一个新的函数,都会拥有一个 prototype 属性,这个属性指向函数的原型对象。
* 所有原型对象都有一个 constructor 属性,指向了 prototype 属性所在函数的指针,即 Cat.prototype.constructor == Cat
* prototype 对象是通过构造函数所产生的实例对象的原型对象,Cat.prototype.loveThing 为 prototype 添加属性,组成了这个prototype 对象。
* 实例对象拥有一个指针 [[prototype]] 指向了它的原型对象 prototype。
```javascript
function Cat(name){
this.name = name;
}
Cat.prototype.loveThing = ['apple'];
Cat.prototype.sayHi = function(){
alert(this.name);
}
var tomcat = new Cat('TOM');
tomcat.sayHi(); // TOM
tomcat.loveThing.push('orange');
alert(tomcat.loveThing); // apple,orange
var redcat = new Cat('RED');
tomcat.sayHi(); // RED
alert(redcat.loveThing); // apple,orange
```
可以看到,上面 tomcat 还有 redcat 作为 Cat 的实例,都 sayHi 出各自构造函数传入的 name。这并不奇怪。而 retcat 的loveThing 和 tomcat 一样,都是 apple,orange ?
当实例 tomcat 调用 tomcat.loveThing 的时候,由于实例中没有 loveThing 这个属性(与其原型对象相同),所以会继续向上搜索,读取到其原型对象的 loveThing 属性。此时执行的 tomcat.loveThing.push(‘orange’),相当于对原型对象进行操作,类似 Cat.prototype.loveThing.push(‘orange’);由于原型对象为所有对象实例 共用 。所以,后来执行了 redcat.loveThing,相当于执行了 Cat.prototype.loveThing;从而得到与 tomcat 相同的结果。
**重写原型对象切断现有原型与任何之前已经存在的对象实例直接的联系,他们的引用仍然是最初的原型。**
```javscript
function Person(){};
var xiaoming = new Person();
Person.prototype = {
constructor: Person,
sayHi: function(){
console.log('hi')
}
}
xiaoming.sayHi(); // err
```
**常用原型语法**
```javascript
function Cat(name){
this.name = name;
}
Cat.prototype = {
constructor: Cat, //重新指向Cat
sayHi: function(){
console.log(this.name);
}
}
var tomCat = new Cat();
console.log(tomCat instanceof Cat); // true
console.log(tomCat instanceof Object); //true
```
上面代码重写了 Cat 的 prototype 属性,由于每一个 prototype 一个对应的 constructor 属性,所以此时 constructor 指向了 Object
```javascript
console.log(tomCat.constructor == Cat); //true
```
当没有对 constructor 重新指向时,会出现下面的情况。
```javascript
console.log(tomCat.constructor == Object); //true
console.log(tomCat.constructor == Cat); //false
```
**for-in 与 hasOwnProperty()**
由于 for-in 方法会将其实例还有原型的属性都输出,可以通过 hasOwnProperty() 这个方法来过滤出来自实例的属性
```javascript
var obj = {name: 123}
for(var key in obj){
if(Object.hasOwnProperty(key)){
// ... 这些key来自实例,非来自原型。
}
}
// 另: es5 中的 object.keys() 方法能够返回所有可枚举属性的字符串数组
```
[转载自(有删改):http://www.alloyteam.com/2015/10/prototype/](http://www.alloyteam.com/2015/10/prototype/)