/*
約定
*/
function Fun(){
//私有屬性
var val = 1; //私有基本屬性
var arr = [1]; //私有引入屬性
function fun(){}; //私有函數(shù)(引用屬性)
//實(shí)例屬性
this.val = 1; //實(shí)例基本屬性
this.arr = [1]; //實(shí)例引用屬性
this.fun = function(){}; //實(shí)例函數(shù)(引用屬性);
};
//原型屬性
Fun.prototype.val = "jack"; //原型基本屬性
Fun.prototype.arr = [1]; //原型引用屬性
Fun.prototype.fun = function(){}; //原型函數(shù)(引用屬性)
js變量可分為兩部分,基本類型和引用類型窘问。
基本類型:基本類型比較簡(jiǎn)單辆童,包括Undefined,Null,Boolean,Number,String,基本類型就是簡(jiǎn)單的數(shù)據(jù)段;
引用類型:引用類型值可能由多個(gè)值構(gòu)成惠赫,引用類型保存在內(nèi)存中把鉴,而js是不能直接訪問(wèn)內(nèi)存的,所以對(duì)于引用類型儿咱,操作的不是實(shí)際的對(duì)象庭砍,而是對(duì)對(duì)象的引用场晶。
一、簡(jiǎn)單原型鏈
function Super(){
this.val = 1;
this.arr = [1];
};
function Sub(){
};
Sub.prototype = new Super();
var Sub1 = new Sub();
var Sub2 = new Sun();
Sub1.val = 5;
Sub1.arr.push(2);
alert(Sub1.val); //5
alert(Sub2.val); //1
alert(Sub1.arr); //1,2
alert(Sub2.arr); //1,2
//可以看到怠缸,修改sub1.arr后sub2.arr也變了诗轻,因?yàn)閬?lái)自原型對(duì)象的引用屬性是所有實(shí)例共享的。
如果不懂凯旭,可查看js基本類型與引用類型詳解
二概耻、借用構(gòu)造函數(shù)
function gou(val){
console.log(typeof val);
this.val = val;
this.arr = [1];
this.fun = function(){
};
};
function fun(val){
gou.call(this,val);
};
var fun1 = new fun(1);
var fun2 = new fun(2);
fun1.arr.push(2);
console.log(fun1.val); //1
console.log(fun2.val); //2
console.log(fun1.arr); //1,2
console.log(fun2.arr); //1
alert(sub1.fun === sub2.fun); // false
借父類的構(gòu)造函數(shù)來(lái)增強(qiáng)子類實(shí)例,等于是把父類的實(shí)例屬性復(fù)制了一份給子類實(shí)例裝上了(完全沒(méi)有用到原型)
缺點(diǎn):
無(wú)法實(shí)現(xiàn)函數(shù)復(fù)用,每個(gè)子類實(shí)例都持有一個(gè)新的fun函數(shù)罐呼,太多了就會(huì)影響性能鞠柄,內(nèi)存爆炸。嫉柴。
P.S.好吧厌杜,剛修復(fù)了共享引用屬性的問(wèn)題,又出現(xiàn)了這個(gè)新問(wèn)題计螺。
三夯尽、組合繼承(最常用)
function zuhe(){
this.val = 1;
this.arr = [1];
};
zuhe.prototype.fun1 = function(){};
zuhe.prototype.fun2 = function(){};
function newSub(){
//通過(guò)call函數(shù)繼承父類的基本數(shù)據(jù)和引用數(shù)據(jù)。
zuhe.call(this); //核心
};
newSub.prototype = new zuhe();
var zh1 = new newSub();
var zh2 = new newSub();
zh1.val = 2;
zh1.arr.push(2);
console.log(zh1); //{val:2,arr:[1,2]}
console.log(zh2); //{val:1,arr:[1]}
console.log(zh1.fun1 === zh2.fun1); //true
把實(shí)例函數(shù)都放在原型對(duì)象上登馒,以實(shí)現(xiàn)函數(shù)復(fù)用匙握。同時(shí)還要保留借用構(gòu)造函數(shù)方式的優(yōu)點(diǎn)
四、寄生組合繼承
組合繼承是js最常用的繼承模式陈轿,組合繼承最大的問(wèn)題就是無(wú)論在什么情況下圈纺,都會(huì)調(diào)用兩次構(gòu)造函數(shù):一次是在創(chuàng)建子類型原型時(shí),另一次是在子類型構(gòu)造函數(shù)內(nèi)部麦射。
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
}
function SubType(name, age){
SuperType.call(this, name); //第二次調(diào)用SuperType()
this.age = age;
}
SubType.prototype = new SuperType(); //第一次調(diào)用SuperType()
SubType.prototype.sayAge = function(){
alert(this.age);
}
在第一次調(diào)用SuperType構(gòu)造函數(shù)時(shí)蛾娶,SubType.prototype會(huì)得到兩個(gè)屬性: name和colors; 他們都是SuperType的實(shí)例屬性潜秋,只不過(guò)現(xiàn)在位于SubType的原型中蛔琅。
當(dāng)調(diào)用SubType構(gòu)造函數(shù)時(shí),又會(huì)調(diào)用一次SuperType構(gòu)造函數(shù)峻呛,這一次又在新對(duì)象上創(chuàng)建了實(shí)例屬性name和colors罗售。
于是這兩個(gè)屬性就屏蔽了原型中的兩個(gè)同名屬性。
寄生組合式繼承就是為了解決這一問(wèn)題杀饵。
function inheritPrototype (subType,superType){
var protoType = Object.create(superType.prototype); //創(chuàng)建對(duì)象
protoType.constructor = subType; //增強(qiáng)對(duì)象
subType.prototype = protoType; //指定對(duì)象
};
function SuperType(name){
this.name = name;
this.colors = ["red","blue","green"];
};
SuperType.prototype.sayName = function(){
alert(this.name);
};
function SubType(name,age){
SuperType.call(this,name);
this.age = age;
};
inheritPrototype(SubType,SuperType);
SubType.prototype.sayAge = function(){
alert(this.age);
};
var instance = new SubType("jack",28);
instance.sayName(); //jack
instance.sayAge(); //28
## 方法二
function beget(obj){ // 生孩子函數(shù) beget:龍beget龍莽囤,鳳beget鳳。
var F = function(){};
F.prototype = obj;
return new F();
}
function Super(){
// 只在此處聲明基本屬性和引用屬性
this.val = 1;
this.arr = [1];
}
// 在此處聲明函數(shù)
Super.prototype.fun1 = function(){};
Super.prototype.fun2 = function(){};
//Super.prototype.fun3...
function Sub(){
Super.call(this); // 核心
// ...
}
var proto = beget(Super.prototype); // 核心
proto.constructor = Sub; // 核心
Sub.prototype = proto; // 核心
var sub = new Sub();
alert(sub.val);
alert(sub.arr);
五切距、原型式
function beget(obj){
var F = function(){};
F.prototype = obj;
return new F();
};
function Super (){
this.val = 1;
this.arr = [1];
};
//拿到父類對(duì)象
var sup = new Super ();
//生孩子
var sub = beget(sup);
//增強(qiáng)
sub.attr1 = 1;
sub.attr2 = 2;
console.log(sub);
console.log(sub.attr1);
console.log(sub.arr);
優(yōu)點(diǎn):
從已有對(duì)象衍生新對(duì)象朽缎,不需要?jiǎng)?chuàng)建自定義類型(更像是對(duì)象復(fù)制,而不是繼承。话肖。)
缺點(diǎn):
原型引用屬性會(huì)被所有實(shí)例共享北秽,因?yàn)槭怯谜麄€(gè)父類對(duì)象來(lái)充當(dāng)了子類原型對(duì)象,所以這個(gè)缺陷無(wú)可避免無(wú)法實(shí)現(xiàn)代碼復(fù)用(新對(duì)象是現(xiàn)取的最筒,屬性是現(xiàn)添的贺氓,都沒(méi)用函數(shù)封裝,怎么復(fù)用)
P.S.這東西和繼承有很大關(guān)系嗎床蜘?為什么尼古拉斯把它也列為實(shí)現(xiàn)繼承的一種方式辙培?關(guān)系不大,但有一定關(guān)系
六邢锯、寄生式
寄生式是一種模式扬蕊,并不是只能用來(lái)繼承
function beget(obj){ // 生孩子函數(shù) beget:龍beget龍,鳳beget鳳丹擎。
var F = function(){};
F.prototype = obj;
return new F();
}
function Super(){
this.val = 1;
this.arr = [1];
}
function getSubObject(obj){
// 創(chuàng)建新對(duì)象
var clone = beget(obj); // 核心
// 增強(qiáng)
clone.attr1 = 1;
clone.attr2 = 2;
//clone.attr3...
return clone;
}
var sub = getSubObject(new Super());
alert(sub.val); // 1
alert(sub.arr); // 1
alert(sub.attr1); // 1
注意:beget函數(shù)并不是必須的尾抑,換言之,創(chuàng)建新對(duì)象 -> 增強(qiáng) -> 返回該對(duì)象蒂培,這樣的過(guò)程叫寄生式繼承再愈,新對(duì)象是如何創(chuàng)建的并不重要(用beget生的,new出來(lái)的护戳,字面量現(xiàn)做的翎冲。。都可以)
優(yōu)點(diǎn):
還是不需要?jiǎng)?chuàng)建自定義類型
缺點(diǎn):
無(wú)法實(shí)現(xiàn)函數(shù)復(fù)用(沒(méi)用到原型媳荒,當(dāng)然不行)
P.S.劇情解析:有缺陷的寄生式繼承 + 不完美的組合繼承 = 完美的寄生組合式繼承府适,不妨回去找找看哪里用到了寄生