屬性拷貝
繼承不單單能通過原型鏈實(shí)現(xiàn)琅攘,也能通過其他方式實(shí)現(xiàn)悉患,屬性拷貝就是其中一種方法。
通過屬性拷貝也能實(shí)現(xiàn)繼承
子對象會(huì)繼承父對象原型鏈上所有的自身屬性
函數(shù)代碼在此:
function extend2(Child,Parent) {
var c = Child.prototype;
var p = Parent.prototype;
// 循環(huán)遍歷 Parent 中的屬性,復(fù)制給 Child
for (var i in p){
c[i] = p [i];
};
// uber 屬性實(shí)現(xiàn)子級(jí)能找到父級(jí)的屬性
Child.uber = Parent.prototype;
}
此時(shí)創(chuàng)建父級(jí)子級(jí)的構(gòu)造函數(shù)及對象棕兼,調(diào)用函數(shù)形成繼承關(guān)系
function Person(){}
Person.prototype.description = "人類";
Person.prototype.age = 0;
Person.prototype.hobby = ["權(quán)利","金錢"]
Person.prototype.say = function(){
return "我的年齡:" +this.age;
}
function Student(){}
// 實(shí)現(xiàn)繼承關(guān)系梅掠,調(diào)用函數(shù)
extend2(Student,Person);
// 此時(shí)不需要更改 construtor 屬性
Student.prototype.description = "學(xué)生";
Student.prototype.age= 18;
var stu = new Student();
console.log(stu.description);//學(xué)生
console.log(stu.say());//我的年齡:18
stu.hobby.pop()
console.log(stu.hobby);//["權(quán)利"]
var per = new Person();
console.log(per.description);//人類
console.log(per.say());//我的年齡:0
console.log(per.hobby);//["權(quán)利"]
此時(shí)實(shí)現(xiàn)了繼承的效果,stu 能訪問 per 的屬性
如圖所示:
注意:該方法只針對于基本數(shù)據(jù)類型有效笑窜,JS中對象的傳遞大多數(shù)都是引用傳遞致燥,僅僅是傳遞對象的地址,子對象修改排截,父對象中也相應(yīng)地會(huì)改變
對象之間的繼承
到目前來說嫌蚤。我們都是通過構(gòu)造函數(shù)實(shí)現(xiàn)的繼承
其實(shí),我們完全可以不使用構(gòu)造函數(shù)就實(shí)現(xiàn)繼承關(guān)系断傲,直接使用對象完成繼承關(guān)系的建立
函數(shù)代碼如下:
function extend3(parent,child) {
// 如果child參數(shù)傳進(jìn)來脱吱,就是外面有已知對象給 child 賦值,
//如果沒有 Child 參數(shù)傳進(jìn)來认罩,函數(shù)會(huì)創(chuàng)建一個(gè)空對象并返回箱蝠,此空對象繼承自 parent
child = child || {};
for (var i in parent){
child[i] = parent[i];
}
child.uber = parent;
return child;
}
現(xiàn)在我們直接創(chuàng)建父級(jí)子級(jí)對象,調(diào)用函數(shù)實(shí)現(xiàn)繼承垦垂,代碼如下:
var per = {
description:"人類",
age:0,
hobby:["金錢","權(quán)利"],
say:function(){
return "我的年齡是:" +this.age;
},
}
function Student(){}
var stu = new Student();
// 建立繼承關(guān)系(讓一個(gè)已知的對象繼承自 per)
extend3(per,stu);
stu.description = "學(xué)生";
console.log(stu.description);//學(xué)生
console.log(stu.age);//0
stu.hobby.pop();
console.log(stu.hobby);//["金錢"]
console.log(stu.say());//我的年齡是:0
console.log(per.hobby);//["金錢"]
// 子對象訪問父對象屬性
console.log(stu.uber.description);//人類
// 創(chuàng)建一個(gè)繼承自 per的對象
var t = extend3(per);
console.log(t.description);
console.log(t.say());
實(shí)現(xiàn)了子級(jí)繼承父級(jí)的效果宦搬,同時(shí),我們還能通過此函數(shù)直接創(chuàng)建子級(jí)對象
深拷貝
上面兩種方式雖然都能調(diào)用函數(shù)劫拗,但是如果修改對象的值间校,是直接修改父級(jí)對象的值,原來父級(jí)的屬性就被替換了
extend2 和 extend3 都是淺拷貝
深拷貝:內(nèi)存拷貝页慷,將內(nèi)存完整的拷貝一份
淺拷貝:引用拷貝憔足,只復(fù)制對象的地址
如果想實(shí)現(xiàn)深拷貝
1.我們會(huì)用hasOwnProperty() 方法判斷該屬性是否需要復(fù)制的
2.子對象不會(huì)影響到父對象中的屬性值
實(shí)現(xiàn)深拷貝的函數(shù)代碼如下:
function deepCopy(parent,child){
child = child || {};
// 遍歷父對象屬性
for(var i in parent){
// 判斷自身屬性
if(parent.hasOwnProperty(i)){
// 判斷自身屬性
if(/*對象類型*/typeof parent[i] === "object"){
// 判斷屬性是否是數(shù)組
child[i] = Array.isArray(parent[i]) ? [] : {};
// 把 parent[i]里的屬性賦值給child[i]里面去
deepCopy(parent[i],child[i]);
}else{
// 基本數(shù)據(jù)類型
child[i] = parent[i];
}
}
}
// child.uber = parent;
return child;
}
這次直接創(chuàng)建對象看看效果
var per = {
description:"人類",
age:0,
hobby:["金錢","權(quán)利"],
say:function(){
return "我的年齡是:" +this.age;
},
}
// t 繼承自 per
var t = deepCopy(per);
// t.description = "學(xué)生";
console.log(t.description);//人類
console.log(per.description);//人類
t.hobby.pop();
console.log(t.hobby);//["金錢"]
console.log(per.hobby);//["金錢", "權(quán)利"]
深淺拷貝的原理
t 如果不修改 description 屬性默認(rèn)繼承父級(jí)屬性,同時(shí)酒繁,t修改數(shù)組滓彰,對 per 的數(shù)組值沒有影響
原型繼承與屬性拷貝的混合應(yīng)用
原型繼承和拷貝繼承混用的方式,能將兩個(gè)父對象的屬性用不同的方式繼承下來
先創(chuàng)建兩個(gè)對象 per 和 base 州袒,作為父對象
代碼如下:
var per = {
description:"人類",
age:0,
hobby:["金錢","權(quán)利"],
say:function(){
return "我的年齡是:" +this.age;
},
}
var base = {
name:"二雷",height:180,weight:100,
}
混用函數(shù)代碼如下:
function extend(p1,p2) {
var child;
var F = function(){};
F.prototype = p1;
child = new F();
for(var i in p2){
child[i] = p2[i];
}
return child;
}
創(chuàng)建子級(jí)揭绑,看一下效果:
var t = extend(per,base);
console.log(t.name);//二雷
console.log(t.hobby);//["金錢", "權(quán)利"]
實(shí)現(xiàn)了
多重繼承
其實(shí),n個(gè)對象的屬性也可以通過函數(shù)實(shí)現(xiàn)繼承于1個(gè)子對象中郎哭,也就是一個(gè)子對象能繼承到那個(gè)對象的屬性
要實(shí)現(xiàn)這個(gè)方法洗做,需要使用函數(shù)的 arguments 屬性
父級(jí)對象的創(chuàng)建:
// 能繼承多個(gè)對象的屬性弓叛,創(chuàng)建多個(gè)對象
var per1 = {
name:"二雷",
}
var per3 = {
age:20,
}
var per5 = {
height:180,name:"雷",
}
var per6 = {
weight:100,
}
繼承函數(shù)的創(chuàng)建:
// arguments類似數(shù)組,它的屬性是當(dāng)前函數(shù)所街搜到的所有參數(shù)
function muli(){
var child = {};
for(var i = 0;i<arguments.length;i++) {
for(var j in arguments[i]){
child[j] = arguments[i][j];
}
}
return child;
}
// 注意:如果父對象中存在相同的屬性诚纸,參數(shù)后面對象中的屬性會(huì)覆蓋前面對象中的屬性
var t = muli(per1,per3,per5,per6);
console.log(t)//Object age: 20 height: 180 name: "雷" weight: 100 __proto__: Object
這樣新建的t對象就有其所有父級(jí)元素的屬性了撰筷,集大成者
不過要注意的是,如果父級(jí)元素中有相同的屬性畦徘,誰在后面輸出誰的屬性毕籽,不過前面的屬性值就被覆蓋了,在當(dāng)前函數(shù)下井辆,不能找回前面的同名屬性值关筒,可以通過設(shè)置 uber 將所有父級(jí)對象屬性保存下來,需要時(shí)再去獲取杯缺。