傳統的OO語言有類的概念,但js(ES5)卻是基於原型實現的面向對象。
原型是?
我們創建的每一個函數都會有一個原型(prototype)屬性,這個屬性是一個指針,指向函數的原型(prototype)對象。使用原型對象可以讓構造函數的實例對象共享原型對象包含的屬性和方法,而不會像工廠模式或者構造函數模式那樣,方法本應該是可以是通用的,可是每一個創建的對象實例都要各自復制一份。
function People(name){ this.name=name; this.sayName=function(){ console.log("Hi! my name is "+this.name); }; }
var Jerel=new People("Jerel"); var AsenZ=new People("AsenZ"); console.log(Jerel.sayName==AsenZ.sayName); //false
但使用原型模式的話,同一個方法是被不同對象實例所引用的,上面的結果會返回ture。
function People(name){ this.name=name; } People.prototype.sayName=function(){ console.log("Hi! my name is "+this.name); }; var Jerel=new People("Jerel"); var AsenZ=new People("AsenZ"); console.log(Jerel.sayName==AsenZ.sayName); //true
構造函數實例化的過程中發生了什么
構造函數通過new操作符實例化的每一個對象,都會生成一個指向構造函數原型對象的指針,不過在這里不是prototype(構造函數通過prototype指針指向原型對象),而是[[prototype]](或者叫—proto—),同時構造函數內部的this指針會綁定到該對象實例上。
構造函數內部綁定在this指針下的所有屬性或者方法會被復制下來,作為實例對象的一部分,但是呢原型中定義的屬性和方法仍是屬於原型自身的,不歸對象實例所有。對象只是引用。對象在調用一個屬性或者方法時,如果在構造函數內部沒有相應的定義,則會通過[[prototype]]指針去原型對象中尋找。
繼承
——借用構造函數
如果需要繼承構造函數內部this指針所綁定的所有屬性和方法的話,可以在子構造函數中使用call方法,call方法的第一個參數會傳入一個對象,這個對象會用來替換調用call方法的方法中的this,所以我們call方法的第一個參數可以傳入子構造函數內部的this指針。
但是這種方法卻繼承不了原型內部的屬性和方法。
function People(name){ this.name=name; } People.prototype.sayName=function(){ console.log("Hi! my name is "+this.name); }; function Boy(name){ People.call(this,name); } var Jerel=new Boy("Jerel"); console.log(Jerel.name); //Jerel console.log(Jerel.sayName); //undefined
如果需要繼承原型對象的內部屬性和方法的話呢?
——組合繼承(借用構造函數+原型鏈)
function People(name){ this.name=name; } People.prototype.sayName=function(){ console.log("Hi! my name is "+this.name); }; function Boy(name){ People.call(this,name); } Boy.prototype=new People(); Boy.prototype.constructor=Boy; var Jerel=new Boy("Jerel"); console.log(Jerel.name); //Jerel Jerel.sayName(); //Hi! my name is Jerel
還有不少其他的實現繼承的方法,不過感覺思路差不了多少的,這一篇總結主要是為了加深一下自己對原型的理解。
雖然說有了babel,現在ES6已經可以放開的使用了!class定義類,extends實現繼承不能再爽,但是這些新特性也不過是語法糖,底層的原理還是離不開最基本的原型。