聖杯模式是Javascript中用來實現繼承的一種方法,它的簡單形式如下所示 這種聖杯模式的本質在於,中間生成了一個對象,起到了隔離的作用,今後為Son.prototype添加屬性時,全部都會加在這個對象裡面,所以不會對父級產生影響。 而向上查找是沿著__proto__查找,可以順利查找到父級的屬性 ...
聖杯模式是Javascript中用來實現繼承的一種方法,它的簡單形式如下所示
<script>
function Father(){}
function Son(){}
Father.prototype.lastName='Jack';
//聖杯模式
function inherit(Target,Origin){
function F(){};
F.prototype=Origin.prototype;
Target.prototype=new F();
}
inherit(Son,Father);
var son=new Son();
var father=new Father();
Son.prototype.sex='male';
console.log(son.lastName);//Jack
console.log(son.sex);//male
console.log(father.sex);//undefined
</script>
這種聖杯模式的本質在於,中間生成了一個對象,起到了隔離的作用,今後為Son.prototype添加屬性時,全部都會加在這個對象裡面,所以不會對父級產生影響。
而向上查找是沿著__proto__查找,可以順利查找到父級的屬性,實現繼承。
下麵來具體分析是如何進行繼承的
首先inherit
函數中定義了一個function F
,F用來充當中間層的函數。
之後F.prototype=Origin.prototype;
這一步表示,讓F和Father的prototype指向同一地址,即Father.prototype。
下一步Target.prototype=new F();
表示,通過函數F生成一個對象(這裡把這個對象成為objF),讓Son函數的prototype指向這個對象objF,到這一步實際上就已經實現了繼承。
下方代碼中,生成了son和father對象之後,對函數Son的prototype增加了一個sex屬性,而Son函數的prototype是指向對象objF的,因此這個sex屬性會加到objF之上,因此下方列印son.sex時就會出現male。
而對於son.lastName而言,首先查找Son函數內部是否有lastName屬性,很明顯沒有。
因此就沿著__proto__(即原型鏈)往上找, 即在函數F中找是否存在該屬性。
之前提到了F和Father的prototype都是指向Father.prototype的,而在Father.prototype中存在該屬性,因此son.lastName的列印結果為Jack。
最後,由於有中間層F的存在,因此Father的prototype自始至終都沒有受到影響,所以father.sex的列印結果為undefined。
一般在使用聖杯模式時還會加上另外兩句話
Target.prototype.constructor=Target;
//由於Target.prototype指向的是objF,沒有constructor這一屬性,沿著__proto__向上查找,發現constructor指向的是Father,因此這裡可以進行歸位,讓它的constructor重新指向它自己
Target.prototype.uber=Origin.prototype;
//uber是超類的意思,這裡主要用來儲存這個目標到底繼承自誰,可寫可不寫
此外,還有另外一種寫法,利用閉包將F作為一個私有化變數,完整代碼如下:
<script>
//方法1
function Father(){}
function Son(){}
Father.prototype.lastName='Jack';
function inherit(Target,Origin){
function F(){};
F.prototype=Origin.prototype;
Target.prototype=new F();
Target.prototype.constructor=Target;
Target.prototype.uber=Origin.prototype;
}
inherit(Son,Father);
var son=new Son();
var father=new Father();
Son.prototype.sex='male';
console.log(son.lastName);//Jack
console.log(son.sex);//male
console.log(father.sex);//undefined
//方法2,利用閉包將F作為一個私有化變數
var inherit=(function(){
var F=function F(){};
return function(){
F.prototype=Origin.prototype;
Target.prototype=new F();
Target.prototype.constructor=Target;
Target.prototype.uber=Origin.prototype;
}
})();
</script>
才疏學淺,如有錯誤敬請指出。