[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/)