JS原型鏈和繼承網上已經爛大街了,5毛能夠買一堆,這裏只提一下:javascript
constructor:普通對象和函數對象都有,指向建立它的函數
prototype: 函數對象纔有,指向構造函數的原型對象(另外一個普通對象)
__proto__: 普通對象和函數對象都有,指向建立它的構造函數的原型對象java
function Fun1(){}; Fun1.prototype.constructor == Fun1 //true var f2 = new Fun1(); f2.__proto__ == Fun1.prototype //true
當實例化新對象時,JS將使用與來自相同構造函數的先前對象相同的初始隱藏類建立它們。
當添加屬性時,對象從一個隱藏類轉換爲另一個隱藏類,一般遵循所謂的「轉換樹」中的先前轉換。例如,若是咱們有如下構造函數:緩存
function fun1(){ this.a = 1; this.b = 2; }
建立過程大體以下(對於普通對象{a:1, b:2}也會有相似的過程):
fun1: M0 {}
|
this.a: M1 {a:?}
|
this.b: M2 {a:?,b:?}函數
在第二次用一樣的構造函數建立一個新的對象實例時(如: var o2 = new fun1()),會複用M0,M1和M2。性能
這種機制有下面幾個優勢:優化
能夠參考以下:
M0
{}
|
M1
{a:?}
/ \
M2 M3
{a:?,b:?} {a:?,c:?}this
與實例的建立過程不一樣,原型的建立過程並不會使用隱藏類,由於原型prototypes一般狀況下是一個惟一的對象,而且不會被其餘的不一樣對象所共享結構。prototype
Prototype建立有二個主要的階段:code
function Foo() {} // Prototype在'創建'模式 var proto = Foo.prototype; proto.method1 = function() { ... } proto.method2 = function() { ... } var o = new Foo(); // 從'創建'模式切換到'使用'模式 o.method1(); // 一樣切換到'使用'模式 proto.method1.call(o);
那麼瞭解上面的原型建立過程有什麼用呢?
JS很難在編譯階段進行代碼分析,即便某個類被用做原型。當咱們發現一個類被當作原型,如:對象
var o = {x:1}; func.prototype = o;
由於原型是能夠改變的,所以以上代碼並不能肯定O被用做原型,除非直到全部步驟結束。
因此:JS不得不首先進行隱藏類的建立過程,並轉化爲原型創建過程,這很是消耗性能。
那麼應該怎麼作呢?以下:
var o = {}; func.prototype = o; o.x = 1;
若是一個對象要做爲原型,那麼儘可能在給對象添加屬性以前就把該對象賦給原型屬性。
更好的方式是下面這種:
func.prototype = Object.create(…); func.prototype.method1 = … func.prototype.method2 = …
最後,優雅的寫法是:
var proto = func.prototype = Object.create(…); proto.method1 = … proto.method2 = …