面向对象(二)
继承
继承是面向对象当中的一个概念,与多态、抽象(封装)共为面向对象的三个基本特征。继承可以使得子类具有父类的属性和方法或者重新定义、追加属性和方法等。
构造函数继承 / 原型继承
构造函数的继承只能用
(apply call bind)
而原型的继承主要分为两种,即拷贝继承和原型继承
- 我们先来看一下构造函数的继承:
//调用父类的构造函数,并且修改 this 指向为当前的实例对象,这样就可以把 父类构造函数中,定义的属性和方法获取过来。
function people(姓名,年龄){
this.姓名 = 姓名;
this.年龄 = 年龄;
}
people.prototype.说话 = function(话语){
console.log(this.姓名+"说"+话语);
}
function HRBP(姓名,年龄){
// 构造函数的继承 (call apply bind)
people.call(this,姓名,年龄);
}
let 张三 = new people("张三",15);
let 李四 = new HRBP("李四",23)
console.log(李四);
- 然后是原型继承以及会出现的一些问题:
第一个思路是把父类的原型之间赋值给子类,虽然子类可以拿到父类的属性,但是这样会导致传址,即当我们再在子类上面修改prototype时,人类的prototype也会受到影响。
function people(姓名,年龄){
this.姓名 = 姓名;
this.年龄 = 年龄;
}
people.prototype.说话 = function(话语){
console.log(this.姓名+"说"+话语);
}
function HRBP(姓名,年龄){
// 构造函数的继承 (call apply bind)
people.call(this,姓名,年龄);
}
HRBP.prototype = people.prototype;
let 张三 = new people("张三",15);
let 李四 = new HRBP("李四",23)
console.log(李四);
李四.说话("我就是李四");
- 所以说这样显然是行不通的,下面我来具体讲一下如何对父类的属性进行拷贝,分为浅拷贝和深拷贝。
浅拷贝
拷贝:复制该对象的属性。把父类中原型中的所有属性拷贝到子类的原型中 而
constructor
本身属于不可枚举的属性 那这样的话就实现了父级的继承而且不会影响父类自身的属性。浅拷贝:只拷贝对象的第一层
function people(姓名,年龄){
this.姓名 = 姓名;
this.年龄 = 年龄;
}
people.prototype.说话 = function(话语){
console.log(this.姓名+"说"+话语);
}
function HRBP(姓名,年龄){
// 构造函数的继承 (call apply bind)
people.call(this,姓名,年龄);
}
HRBP.prototype.job = function(){
console.log("公司经营计划");
}
HRBP.prototype = people.prototype;
function extend(father,child){
for(let s in father.prototype){
child.prototype[s] = father.prototype[s];
}
}
extend(people,HRBP);
let 张三 = new people("张三",15);
let 李四 = new HRBP("李四",23)
console.log(李四);
console.log(张三);
李四.说话("我就是李四");
- 但是浅拷贝也还是会有一些问题,比如父类比较复杂,可能不只含有基本类型的属性,还会有一些引用类型Object(如数组Arr或者对象),此时使用浅拷贝继承的话,又会把这个引用类型的属性直接传给子类,这样就又重新造成了传址的问题,所以只拷贝一层显然是不行的,于是深拷贝就很好的解决了该问题。
深拷贝
深拷贝: 循环对象的每一层,只要该属性的值是对象类型就进行拷贝。
通过刚刚对浅拷贝的安全性问题分析我们发现,使用深拷贝原型继承会更安全。总结一下深拷贝的过程:先传入数据进行拷贝,然后对数据进行判断,如果是基本类型数据那就直接返回该属性,如果是引用类型,那就再判断是对象格式的数据还是数组格式的数据,然后通过递归沿着对象或数组的属性一层一层向下拷贝(每一层都进行拷贝),把基本类型的数据拷贝给子类,这样就不会发生传址问题。
let father = {
name: "姓名",
age: 48,
child:[
{
name: "小明",
age: 20
},{
name: "小红",
age: 15
}
],
salary:[
10000,
20000,
25000
]
};
/*
copy(father)
*/
function copy(data){// 传入数据,拷贝该数据
if(typeof data === "object"){ // 如果是一个引用类型就接着进行拷贝
// 引用类型的数据常用有两种:数组 和 对象
return Array.isArray(data)?copyArr(data):copyObj(data);
} else {
return data;
}
}
function copyArr(arr){ // 对数组格式的数据进行拷贝
let newArr = [];
for(var i = 0; i < arr.length; i++){
// arr[i] 有可能是基本类型也有可能是引用类型
newArr.push(copy(arr[i]));
}
return newArr;
}
function copyObj(obj){ // 对对象格式的数据进行拷贝
let newObj = {};
for(var s in obj){
newObj[s] = copy(obj[s]);
}
return newObj;
}
let child = copy(father);
child.child[0].age = 24;
console.log(child);
console.log(father);