属性拷贝
继承不单单能通过原型链实现,也能通过其他方式实现,属性拷贝就是其中一种方法。
通过属性拷贝也能实现继承
子对象会继承父对象原型链上所有的自身属性
函数代码在此:
function extend2(Child,Parent) {
var c = Child.prototype;
var p = Parent.prototype;
// 循环遍历 Parent 中的属性,复制给 Child
for (var i in p){
c[i] = p [i];
};
// uber 属性实现子级能找到父级的属性
Child.uber = Parent.prototype;
}
此时创建父级子级的构造函数及对象,调用函数形成继承关系
function Person(){}
Person.prototype.description = "人类";
Person.prototype.age = 0;
Person.prototype.hobby = ["权利","金钱"]
Person.prototype.say = function(){
return "我的年龄:" +this.age;
}
function Student(){}
// 实现继承关系,调用函数
extend2(Student,Person);
// 此时不需要更改 construtor 属性
Student.prototype.description = "学生";
Student.prototype.age= 18;
var stu = new Student();
console.log(stu.description);//学生
console.log(stu.say());//我的年龄:18
stu.hobby.pop()
console.log(stu.hobby);//["权利"]
var per = new Person();
console.log(per.description);//人类
console.log(per.say());//我的年龄:0
console.log(per.hobby);//["权利"]
此时实现了继承的效果,stu 能访问 per 的属性
如图所示:
注意:该方法只针对于基本数据类型有效,JS中对象的传递大多数都是引用传递,仅仅是传递对象的地址,子对象修改,父对象中也相应地会改变
对象之间的继承
到目前来说。我们都是通过构造函数实现的继承
其实,我们完全可以不使用构造函数就实现继承关系,直接使用对象完成继承关系的建立
函数代码如下:
function extend3(parent,child) {
// 如果child参数传进来,就是外面有已知对象给 child 赋值,
//如果没有 Child 参数传进来,函数会创建一个空对象并返回,此空对象继承自 parent
child = child || {};
for (var i in parent){
child[i] = parent[i];
}
child.uber = parent;
return child;
}
现在我们直接创建父级子级对象,调用函数实现继承,代码如下:
var per = {
description:"人类",
age:0,
hobby:["金钱","权利"],
say:function(){
return "我的年龄是:" +this.age;
},
}
function Student(){}
var stu = new Student();
// 建立继承关系(让一个已知的对象继承自 per)
extend3(per,stu);
stu.description = "学生";
console.log(stu.description);//学生
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);//人类
// 创建一个继承自 per的对象
var t = extend3(per);
console.log(t.description);
console.log(t.say());
实现了子级继承父级的效果,同时,我们还能通过此函数直接创建子级对象
深拷贝
上面两种方式虽然都能调用函数,但是如果修改对象的值,是直接修改父级对象的值,原来父级的属性就被替换了
extend2 和 extend3 都是浅拷贝
深拷贝:内存拷贝,将内存完整的拷贝一份
浅拷贝:引用拷贝,只复制对象的地址
如果想实现深拷贝
1.我们会用hasOwnProperty() 方法判断该属性是否需要复制的
2.子对象不会影响到父对象中的属性值
实现深拷贝的函数代码如下:
function deepCopy(parent,child){
child = child || {};
// 遍历父对象属性
for(var i in parent){
// 判断自身属性
if(parent.hasOwnProperty(i)){
// 判断自身属性
if(/*对象类型*/typeof parent[i] === "object"){
// 判断属性是否是数组
child[i] = Array.isArray(parent[i]) ? [] : {};
// 把 parent[i]里的属性赋值给child[i]里面去
deepCopy(parent[i],child[i]);
}else{
// 基本数据类型
child[i] = parent[i];
}
}
}
// child.uber = parent;
return child;
}
这次直接创建对象看看效果
var per = {
description:"人类",
age:0,
hobby:["金钱","权利"],
say:function(){
return "我的年龄是:" +this.age;
},
}
// t 继承自 per
var t = deepCopy(per);
// t.description = "学生";
console.log(t.description);//人类
console.log(per.description);//人类
t.hobby.pop();
console.log(t.hobby);//["金钱"]
console.log(per.hobby);//["金钱", "权利"]
深浅拷贝的原理
t 如果不修改 description 属性默认继承父级属性,同时,t修改数组,对 per 的数组值没有影响
原型继承与属性拷贝的混合应用
原型继承和拷贝继承混用的方式,能将两个父对象的属性用不同的方式继承下来
先创建两个对象 per 和 base ,作为父对象
代码如下:
var per = {
description:"人类",
age:0,
hobby:["金钱","权利"],
say:function(){
return "我的年龄是:" +this.age;
},
}
var base = {
name:"二雷",height:180,weight:100,
}
混用函数代码如下:
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;
}
创建子级,看一下效果:
var t = extend(per,base);
console.log(t.name);//二雷
console.log(t.hobby);//["金钱", "权利"]
实现了
多重继承
其实,n个对象的属性也可以通过函数实现继承于1个子对象中,也就是一个子对象能继承到那个对象的属性
要实现这个方法,需要使用函数的 arguments 属性
父级对象的创建:
// 能继承多个对象的属性,创建多个对象
var per1 = {
name:"二雷",
}
var per3 = {
age:20,
}
var per5 = {
height:180,name:"雷",
}
var per6 = {
weight:100,
}
继承函数的创建:
// arguments类似数组,它的属性是当前函数所街搜到的所有参数
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;
}
// 注意:如果父对象中存在相同的属性,参数后面对象中的属性会覆盖前面对象中的属性
var t = muli(per1,per3,per5,per6);
console.log(t)//Object age: 20 height: 180 name: "雷" weight: 100 __proto__: Object
这样新建的t对象就有其所有父级元素的属性了,集大成者
不过要注意的是,如果父级元素中有相同的属性,谁在后面输出谁的属性,不过前面的属性值就被覆盖了,在当前函数下,不能找回前面的同名属性值,可以通过设置 uber 将所有父级对象属性保存下来,需要时再去获取。