JS 作为面向对象的弱类型的语言,继承是它非常强大的特征之一
JS继承实现的方式:
1.原型链继承
2.构造函数继承
3.实例继承
4.拷贝继承
5.组合继承和寄生继承
原型链继承:
核心:将父类的实例作为子类的原型
function Animal(name){//创建一个父类 Animals
this.name = name;//属性
this.sleep = function(){//方法
return this.name+"在睡觉";
}
}
//给父类 Animal 加一个原型方法:
Animal.prototype.eat = function(food){
return this.name + "正在吃" + food;
}
//原型链继承核心: 将父类的实例作为子类的原型
function Cat(age){
this.age = 12;
}//创建一个子类
Cat.prototype = new Animal();//把Animal父类的实例赋给子类的原型
//若子类需要新增属性和方法必须放在它的后边
Cat.prototype.name = "cat";
//给子类新增一个原型属性name 相当于是 Animal父类的 this.name = "cat"; 但不会影响父类
Cat.prototype.run = function(){
return this.name + this.age + this.sleep();
}
var cat = new Cat();
console.log(cat.name);//cat
console.log(cat.eat("fish"));//cat正在吃fish
console.log(cat.sleep());//cat在睡觉
console.log(cat instanceof Cat);//turn
console.log(cat instanceof Animal);//turn
console.log(cat.run());//cat12cat在睡觉
原型链继承的特点和缺点:
特点:
1.非常纯粹的继承关系,它实例既是子类的实例,也是父类的实例
2.父类新增的原型方法和属性,子类都是可以访问的到的
3.简单,方便
缺点:
1.如果子类要新增属性和方法,必须要放在new 父类()这2.样的语句之后执行
3.无法实现多继承的
4.来自原型的所有属性被所有实例所共享
5.创建子类实例时,无法向父类构造函数传参
构造函数的继承:
使用父类的构造函数来增强子类的实例,其实就是复制父类的实例属性给子类,不用到原型链
function Animal(name){//创建一个父类 Animals
this.name = name;//属性
this.sleep = function(){//方法
return this.name+"在睡觉";
}
}
function Dog(name){
Animal.call(this);
//属性使用对象冒充,其实就是改变this指向,继承父类
//this 相当于 new Animal
this.name = name;
Cat.call(this);//可以实现多个父类继承
}
var dog = new Dog("dog");
console.log(dog.name);//dog
console.log(dog.sleep())//dog在睡觉
console.log(dog.name);//dog
console.log(dog.age);//12
//console.log(dog.eat("狗粮"));//无法访问父类原型对象上的方法
console.log(dog instanceof Dog);//ture
console.log(dog instanceof Animal);//false,不能继承父类的原型对象
构造函数继承的特点和缺点:
特点:
1.创建子类实例的时候,可以向父类传递参数
2.可以实现多个继承(call多个父类对象)
缺点:
1.实例是子类的实例,不是父类的实例
2.只能继承父类实例的属性和方法,不能继承父类的原型属性和方法
3.无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
实例继承:
为父类的实例添加新的属性,作为子类实例的返回值
function Animal(name){//创建一个父类 Animals
this.name = name;//属性
this.sleep = function(){//方法
return this.name+"在睡觉";
}
}
//给父类 Animal 加一个原型方法:
Animal.prototype.eat = function(food){
return this.name + "正在吃" + food;
}
function Sheep(name){
var animal = new Animal();//把父类的实例赋给animal
animal.name = name;//给实例添加属性
return animal;//返回属性值 作为子类的实例返回,但还是属于父类的实例
}
var sheep = new Sheep("Xiaoen");
//var sheep = Sheep("Xiaoen");
console.log(sheep.name);//Xiaoen
console.log(sheep.sleep());//Xiaoen在睡觉
console.log(sheep.eat("草"));//Xiaoen在吃草
console.log(sheep instanceof Sheep);//false
console.log(sheep instanceof Animal);//ture
实例继承的特点和缺点:
特点:
不限制调用方式,不管是new 子类()还是直接子类(),返回的对象都具有相同的效果
缺点:
实例是父类的实例,不是子类的实例
不支持多继承,因为函数的返回值只有一个
拷贝继承:(少用)
支持多继承,效率低,内存占用高(因为要拷贝父类的属性和方法)
没有办法获取父类不可枚举的方法(不可枚举的方法不能使用for in 遍历)
寄生组合继承:
核心:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造函数时,
就不会初始化两次实例的方法和属性,避免了组合继承的缺点
核心:通过调用父类构造,继承父类的属性并且保留传参的优点,
然后通过将父类实例作为子类原型,实现函数复用
function Animal(name){//创建一个父类 Animals
this.name = name;//属性
this.sleep = function(){//方法
return this.name+"在睡觉";
}
}
function Rubbit(name){
Animal.call(this);
this.name = name;
}
Rubbit.prototype = new Animal();
Rubbit.prototype.constructor = Rubbit;//组合继承需要修复构造函数的指向
var rubbit = new Rubbit("小白兔");
console.log(rubbit.name);//小白兔
console.log(rubbit.sleep());//小白兔在睡觉
console.log(rubbit instanceof Rubbit);//true
console.log(rubbit instanceof Animal);//true
组合继承的特点和缺点:
特点:
实例既是父类的实例,也是子类的实例
可以继承实例的方法和属性,也可以继承原型的方法和属性
可以传参
函数可以重复使用
缺点:
调用了两次父类构造函数,生成了两份实例(子类的实例将子类原型的实例屏蔽掉了)
寄生组合继承:
核心:通过寄生方式,砍掉父类的实例属性,这样,在调用两次父类的构造函数时,
就不会初始化两次实例的方法和属性,避免了组合继承的缺点
function Animal(name){//创建一个父类 Animals
this.name = name;//属性
this.sleep = function(){//方法
return this.name+"在睡觉";
}
}
//给父类 Animal 加一个原型方法:
Animal.prototype.eat = function(food){
return this.name + "正在吃" + food;
}
function Pig(name){
Animal.call(this);
this.name = name;
}
(function(){
var obj = function(){}//创建一个没有实例方法的类
obj.prototype = Animal.prototype;
//将父类的原型传给这个没有实例方法的类的原型
Pig.prototype = new obj();//将实例作为子类的原型
})();
Pig.prototype.constructor = Pig;//修复构造函数指向
var pig = new Pig('佩奇');
console.log(pig.name);//佩奇
console.log(pig.sleep());//佩奇在睡觉
console.log(pig.eat("垃圾"));//佩奇在吃垃圾
console.log(pig instanceof Pig);//true
console.log(pig instanceof Animal);//true
特点:完美
缺点:实现起来比较复杂
网友评论