<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>對象的繼承</title>
</head>
<body>
<script>
1.什麼是繼承
1>:在原有對象的基礎上,略作修改,得到一個新的對象
2>:不影響原有對象的功能
2.如何添加繼承
1>:屬性: call
2>:方法: for in (拷貝繼承)
3.例子
1>: 拖拽
主題 :
繼承 :子類不影響父類,子類可以繼承父類的一些功能(代碼的複用)
首先我們寫一個面向對象的例子(有屬性,有方法):
function CreatePerson(name,sex){ // 父類
this.name = name;
this.age = age;
}
CreatePerson.prototype.showName = function(){
alert(this.name);
}
var p1 = new CreatePerson('小明','男');
p1.showName(); // 小明
然後我們來實現繼承:
function CreateStar(name,sex,job){ // 子類
CreatePerson(name,sex); // 錯誤寫法
this.job = job;
}
該例子中我們繼承就是爲了實現 屬性name和sex的繼承,我們可能會認爲直接在子類的構造函數中調用父類的構造函數,
這樣做是沒錯,但是存在作用域的問題, 子類中調用的父類構造函數是屬於window對象的,那麼添加的兩個屬性name和
sex 就屬於window對象,而不屬於子類對象,那我們該怎麼做呢?我們可以通過使用call方法,修改父類的作用域
正確寫法應該是:
function CreateStar(name,sex,job){ // 子類
// 這樣就可以正確的繼承父類的屬性了
CreatePerson.call(this,name,sex);
this.job = job ;
}
總結 : 屬性繼承的方式 -> 採用call的形式調用父類的構造函數
那麼我們該如何實現方法的繼承呢 ?如demo中 我們想把父類的 showName()方法給繼承過來,該怎麼辦呢?
想到一種方法 : 把父類的原型 賦值給 子類的原型
既 : CreateStar.prototype = CreatePerson.prototype;
此時子類對象就擁有了父類對象的所有方法如 :
var p2 = new CreateStar('黃曉明','男','演員');
p2.showName(); // 黃曉明
子類此時可以調用父類的方法,並輸出正確結果,說明此時我們已經實現了子類方法的繼承。
但是這裏存在一點小問題,我們會發現:
我們把一個原型對象賦值給另外一個原型對象,這是一種對象的引用(對象賦值給對象,這是一種引用關係),
對象的引用會造成他們值的地址在內存的同一個地址上,這樣的話如果我們修改了其中一個對象,
另外一個對象也會被修改.
如下 我們給子類對象添加一個方法:
CreateStar.prototype.showJob = function(){};
此時我們在控制檯發現,父類對象也有了一個showJob()方法,既我們無意間修改了父類,這顯然不符合繼承的規則,
繼承的時候不能影響原有對象的功能,那我們該怎麼解決這個問題呢?
解決辦法 : 對對象進行復制,而不是引用
下面我們來看demo2:
var a ={
name : 小明
};
// 對象a賦值給對象b,對象賦值給對象,這是一種引用關係,此時他們的值會指向內置中的同一個地址,
// 當我們修改其中一個對象時,另外一個對象也會被修改
var b =a;
b.name = '小強';
alert(a.name); // 小強
我們會發現,將對象a 賦值給對象b,然後修改對象b的name屬性,結果對象a的name 屬性值也被修改了,
顯然這不是我們想要的.怎麼解決呢?
我們可以通過基本類型值賦值的形式來解決這個問題(值傳遞的方式)
demo1:
代碼:
var a = {
name : '小明'
};
var b ={};
function extend(obj1, obj2){ // 封裝函數
for(var attr in obj2){
obj1[attr] = obj2[attr];
}
}
extend(b,a);
b.name = '小強';
alert(a.name); // 小明
通過 for - in 遍歷,使用基本類型值賦值的形式很好地解決了我們這個問題.
這種方法也可以用來解決我們最上面的問題(CreatePerson對象與CreateStar對象方法的繼承問題)
demo2:
function CreatePerson(name,sex){ // 父類
this.name = name;
this.sex = sex;
}
CreatePerson.prototype.showName= function(){
alert(this.name);
}
function CreateStar(name,sex,job){ // 子類
CreatePerson.call(this,name,sex);
this.job = job;
}
function extend(obj1,obj2){ // 方法繼承的封裝函數
for(var attr in obj2){
obj1[attr]= obj2[attr];
}
}
extend(CreateStar.prototype,CreatePerson.prototype); // 方法繼承
// 此時我們就可以實現了 CreateStar對CreatePerson 的繼承,同時修改他們中的任意一個,
// 對另外一個也沒有影響
那麼問題又來了, 對於demo1中,name的值是字符串,是基本類型,這樣解決沒問題,可是 通過for in 遍歷的
項,(CreatePerson.prototype和CreatePerson.prototype),他們裏面是函數,我們前面介紹過,函數是對象類型,
那麼函數屬於對象類型,我們通過for-in遍歷原型對象,然後賦值(函數賦給函數)爲什麼不會出現問題呢?
其實呢,函數雖然是對象類型,但是他跟對象類型還是有點區別的,函數呢它其實是不能被修改的,它只能被改變(賦值)
什麼意識呢? 我們看下面的例子
demo3: (這裏是修改,修改後會影響另外一個對象)
var a = [1,2,3];
var b = a;
b.push(4);
alert(a); // 1,2,3,4
demo4: (這裏是重新賦值,對象b又重新生成了,所以他跟對象a之間的引用鏈條就斷開了,它倆之間就沒關係了)
var a = [1,2,3];
var b = a;
b = [1,2,3,4];
alert(a); // 1,2,3
因爲: 函數雖然是對象類型,但是隻要你一賦值,它必然不會出現相互影響,因爲它沒法修改,只能賦值故,
根據函數的這個特點我們可以利用for -in 實現對象方法的繼承
總結: 函數的特點: 函數雖然是對象類型,但是函數是不能修改的,函數只能重新賦值
總結 : 方法的繼承方法 : 使用 for in, 這種方法也叫做拷貝繼承(JQuery也是採用的拷貝繼承,當然它的
實現比較複雜點)
</script>
</body>
</html>
高級面向對象 之 繼承(拷貝繼承)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.