一、什么是继承
官方的定义:
A对象通过继承 B 对象,就能直接拥有 B 对象的所有属性和方法。
二、继承的实现方式
1、原型式继承
原型式继承指的是:将父类的实例作为子类的原型(需要将类的构造器重置)
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.say=function(){
return this.name+','+this.age;
}
var p = new Person("法外狂徒张三",15);
function Student(name,age,school){
Person.call(this,name,age); //借用构造函数
this.school = school;
}
Student.prototype = Object.create(Person.prototype);
//相当于子类的原型等于父类的实例
Student.prototype.constructor= Student; //改变子类的构造器
Student.prototype.say=function(){
//Person.prototype.say.call(this)得到父类的方法
return Person.prototype.say.call(this)+","+this.school;
}
var s = new Student("法外狂徒张三","15","清华")
console.log(s.say())
2、构造函数式继承
子类拿不到父类原型上的方法(不需要将类的构造器重置)
构造函数式继承主要是利用了call()和apply()两个方法,想要明白构造函数式继承先得弄明白这两个方法是怎么回事。
apply():调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.apply(A, arguments);即A对象应用B对象的方法。
call():调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.call(A,args1,args2);即A对象调用B对象的方法。
/*apply()方法*/
function.apply(thisObj[, argArray])
/*call()方法*/
function.call(thisObj[, arg1[, arg2[, [,...argN]]]]);
当然,仅仅靠上面的基本解释与代码还是不能理解它们是做什么的。
下面我们看下它们的基本用法。
// 求和
function getSum(num1,num2){
return num1+num2;
}
// 求差
function getDiff(num1,num2){
return num1-num2;
}
// getDiff调用getSum, 特点:里面调用外面
getSum.apply(getDiff,[10,5]);// 15
// getSum调用getDiff
getDiff.apply(getSum,[10,5]);// 5
// getDiff调用getSum, 特点:里面调用外面
getSum.call(getDiff,10,5);// 15
// getSum调用getDiff
getDiff.call(getSum,10,5);// 5
由以上的代码我们可以发现,这俩函数的特点都是里面调用外面,真正执行的都是call()或apply()方法的调用者。
弄明白这俩函数的使用方式后我们就可以实现我们需要的构造函数式继承了。
// 父类
function Person(name){
this.name = name||'法外狂徒张三';
this.sayHello = function(){
alert('hello');
}
}
Person.prototype.sayGoodbye = function(){
alert('goodbye');
}
// 子类
// 使用父类的构造函数来增强子类,等于赋值父类的实例属性给子类(不用原型)
function Student(name){
// 使用call()方法改变了this指向
Person.call(this);
this.name = name;
}
let stu = new Student('法外狂徒李四');
stu.name;// '法外狂徒张三'
stu.sayHello();
stu.sayGoodbye();
//关于apply方法我们还可以衍生出一些小技巧。
// 利用数学对象的max方法迅速得到数组中的最大数
let arr = [10,2,3,8,17,22,8,21,14];
// 第一个参数null表示不需要改变任何对象的this
console.log(Math.max.apply(null,arr));
3、组合式继承
组合式继承利用的就是原型式继承和构造函数式继承(需要将类的构造器重置)
// 组合式继承:原型+构造, instanceof,一个实例属于两个类
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.show = function(){
console.log(`我是${this.name},我${this.age}岁了`);
}
function Student(name,age){
Person.call(this,name,age);
}
Student.prototype = new Person();
Student.prototype.constructor = Student;
let s1 = new Student('法外狂徒张三',20);
console.log(s1);
s1.show();
4、Es6继承
class Person {
constructor(name,age){
this.name = name;
this.age = age;
}
say(){ //Person.prototype
return this.name+','+this.age;
}
}
class Student extends Person {
constructor(name,age,school){
super(name,age); //获得父类的属性
this.school = school
}
say(){
return super.say()+","+this.school; //super.say()继父类的方法
}
}
var s = new Student("法外狂徒张三",15,"清华");
console.log(s.say());
5、拷贝式继承
拷贝式继承也是我们常用的一种手段,但是拷贝也有区分:
浅拷贝:直接赋值拷贝。
深拷贝:将A对象的属性全部复制到B对象上。
①浅拷贝
引用赋值 一改全改
letobj1 = {
name:'tom',
age:20
};
// 浅拷贝,直接赋值拷贝
let obj2 = obj1;
obj2.height = '180cm';
obj1;// {name:'tom',age:20,height:'180cm'}
obj2;// {name:'tom',age:20,height:'180cm'}
②深拷贝
递归赋值
let obj1 = {
name:'tom',
age:20,
hobby:['game','eat']
};
// 定义递归函数
function deepClone(obj){
let objClone = Array.isArray(obj)?[]:{};
if( obj && typeof obj==='object' ){
for( let key in obj ){
// 如果是引用数据类型,就使用递归
if(obj[key] && typeof obj[key]==='object'){
objClone[key] = deepClone(obj[key]);
}else{
// 如果是基本数据类型就直接复制
objClone[key] = obj[key]
}
}
}
return objClone;
}
let obj2 = deepClone(obj1);
obj2.hobby.push('haha');
console.log(obj1);
console.log(obj2);
拷贝式继承在之前已详细讲解,因此不在这里赘述,想了解更多的深浅拷贝内容请移步
精致的深浅拷贝
今天博主就总结到这了,有什么想说的是评论区见~