携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天juejin.cn/post/712312…
继承的方式
- 原始对象继承
- 原型链继承
- 构造函数基础
- 组合继承(原型链和构造函数)
- 寄生组合继承
原始对象继承
- 核心:通过new关键字继承js原生构造函数Object的属性方法
- 语法: var obj=new Object();
原型链继承
核心:将父类的实例作为子类的原型。
/ 把父类的实例对象作为子类的原型对象
function Person(n,a){
this.name=n;
this.age=a;
Person.prototype.sayName=function(){
Alert(“使用原型得到name:”+this.name);
}
}
function Aa(g){
this.gard=g;
}
Aa.prototype=new Person("tom",20);//要把这一步放在构造器外面,否则访问不到属性值
Aa.prototype.intr=function(){alert(this. gard)}//为子类新增方法,要放在new Person后面,并且不能放在构造器内。
var b=new Aa("5年级");
console.log(b.name)//通过原型链可访问到b继承来的name属性值
console.log(b);
b. sayName ();//输出:使用原型得到name:tom
b.intr()//输出:5年级
原型链特点:
- 非常纯粹的继承关系,实例是子类的实例,也是父类的实例
- 父类新增原型方法,子类都能访问到
- 简单、易于实现
原型链缺点:
- 要想为子类新增属性和方法,必须要在new Person()这样的语句之后执行,不能放到构造器中
- 无法实现多继承(只能有一个子类)
- 来自原型对象的引用属性是所有实例共享的
- 创建子类实例时,无法向父类构造器传参
在解决原型中包含引用类型值所带来的问题,使用构造函数技术来解决。
构造函数继承
核心:使用父类的构造函数来增强子类实例,等于是复制父类的属性给子类(没有用到原型)
// 方法一
//创建一个构造函数父类
function ClassA(sColor){
this.color=sColor;
this.sayColor=function(){
alert(this.color)
}
}
//子类
function ClassB(bColor,bName){
this.newMethod=ClassA; //把父类事例(函数名)作为子类的属性值
this.newMethod(bColor);//通过子类的实例对象调用父类构造函数,改变其中this的指向。
delete this.newMethod;
this.name=bName;
this.sayName=function(){
alert(this.name);
}
}
var b=new ClassB("red","lily");
console.log(b);
b.sayName()
b.sayColor()
console.log(b.name)
//方法二
function Animal(name){
this.name=name;
this.showName=function(){
alert(this.name)
}
}
function Cat(name){
Animal.call(this,name);//以前这里是把父类构造函数名作为子类的属性值,并进行调用
//现在直接改变了showName里面this的指向
}
var cat=new Cat("Black Cat");
cat.showName()
console.log(cat)
构造继承特点:
- 解决了原型链继承中,子类实例共享父类引用属性的问题。
- 创建子类实例时,可以向父类传递参数
- 可以实现多继承(call多个父类对象) 缺点:
- 只能继承父类实例的属性和方法,不能继承原型属性/方法
- 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能。()
- 每次调用子类的时候,都会调用父类的方法。
组合继承
- 组合继承,指的是将原型链和构造函数的技术组合到一起
- 思路:是使用原型链实现对原型方法的继承,而通过借用构造函数来实现对实例属性的继承。
- 这样,既通过在原型上定义方法实现了函数的复用,又能够保证每个实例都有它自己的属性。
// 2、又能够保证每个实例都有自己的属性
function Person(n,a){
this.name=n;
this.age=a;
}
//1、通过在原型上定义方法实现复用
Person.prototype.sayName=function(){
alert(this.name)
}
function Student(sn,sa,g){
Person.call(this,sn,sa)//改变了sayName方法里面this的指向,指向了当前构造函数里的this,这里也是对Person这个函数的调用,并把sn,sa这两个参数传了进去
this.gard=g;
}
Student.prototype=new Person();//在这里又调用了一次Person这个构造函数,因为Student的原型是Person的实例,是个对象,对象没有construstor这个属性,所以再指回Student的原型里构造函数是Student自己本身
Student.prototype.constructor=Student;//
var s1=new Student("张三",20,3)
console.log(s1)
s1.sayName()
var s2=new Student("李四",18)
s2.sayName()
console.log(s1.name)//张三
console.log(s2)
组合继承特点:
- 弥补了构造继承的缺点,可以继承实例的属性和方法,也可以继承原型属性和方法
- 既是子类的实例,也是父类的实例
- 不存在引用属性共享问题
- 可传参
- 函数可复用
缺点:无论在什么情况下,都会调用两次父类 构造函数,一次是在创建子类原型的时候,一次是在子类构造函数的内部(即改变指针的方向)
寄生组合继承
核心:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造函数的时候,就不会初始化两次实例方法/属性,避免了组合继承的缺点。
// 核心:通过寄生方法,砍掉父类的实例属性,这样,在调用两次父类的构造的时候
//就不会初始化两次实例方法/属性,避免了组合继承来的缺点
// 混合模式 ——父类
function Animal(name){
this.name=name;
}
Animal.prototype.showName=function(){//Animal的原型里只有一个showName方法,没有任何属性
alert(this.name)
}
// 构造函数 ——子类
function Cat(number,name){
Animal.call(this,name)
this.num=number;
}
// 因为组合模式会调用两次Animal函数,而且不仅继承里面的方法,还有属性
(function (){
//创建一个没有实例方法的类
var Super=function(){};
Super.prototype=Animal.prototype;//这时候Super原型里也只有showName一个方法
Cat.prototype=new Super();//Super实例里面也只有一个继承来的方法showName
})();
Cat.prototype.constructor=Cat;
var cat=new Cat(10,"狗");
cat.showName();
alert(cat.num)
console.log(cat)
寄生组合继承特点:只调用了一次supertype构造函数,因此避免在suptype.prototype上创建不必要的,多余的属性,与此同时,原型链还能保持不变,还能正常使用instancof和isPrototypeOf(),因此,寄生组合式继承被认为是引用类型最理想的继承方式。 缺点:实现较为复杂。