JavaScript继承学习总结(面试篇)
2020-04-14 本文已影响0人
Mstian
说继承之前先说几个概念。
原型和原型链
在JS中万物皆对象,对象有分函数对象(Function
),普通对象(Object
)。
任何对象都具有隐式原型属性__proto__
,只有函数对象有显示原型属性prototype
。
原型链:原型链是针对原型对象的,在查找实例属性时,先在实例中查找,如果没有找到,接着再去原型上查找,如果还没有找到就去其父级原型上查找,一直向上直到找到或者属性不存在返回null。
作用域链:作用域链是针对变量的,先在自己的变量范围内查找,找不到再沿着作用域向上一直查找。
继承:通过某种方式让一个对象可以访问到另一个对象中的属性和方法,我们把这种方式称之为继承 ,继承的好处就是可以实现属性方法共用。
继承方式
- 原型链继承(将父类的实例作为子类的原型)
function Animal(){
this.title="animal";
this.say = function(){
console.log(this.title + '--' + this.name +"---"+this.type)
}
}
function Cat(name,type){
this.name = name;
this.type = type;
}
Cat.prototype = new Animal();
Cat.prototype.eat = "鱼";
let cat = new Cat("猫咪","喵喵");
cat.say(); //animal--猫咪---喵喵
- 构造继承(使用父类的构造函数来增强子类实例,等于是复制父类的实例属性或方法给子类)
function Animal(){
this.title="animal";
this.say = function(){
console.log(this.title + '--' + this.name +"---"+this.type)
}
}
Animal.prototype.eat = "骨头"
function Dog(name,type){
Animal.call(this);
this.name = name;
this.type = type
}
let dog = new Dog("lucy",'汪汪汪');
dog.say(); //animal--lucy---汪汪汪
- 实例继承(为父类实例添加新特性,作为子类实例返回)
function Animal(){
this.title="animal";
this.say = function(){
console.log(this.title + '--' + this.name +"---"+this.type)
}
}
function Pig(name,type){
var instance = new Animal();
instance.name = name;
instance.type = type;
return instance;
}
var pig = new Pig("猪猪","哼哼哼");
pig.say() // animal--猪猪---哼哼哼
- 拷贝继承(将父级可枚举方法以及属性拷贝至子级原型中)
function Animal(){
this.title="animal";
this.say = function(){
console.log(this.title + '--' + this.name +"---"+this.type)
}
}
function Bird(name,type){
var animal = new Animal();
for(var key in animal){
Bird.prototype[key] = animal[key]
}
Bird.prototype.name = name;
Bird.prototype.type = type;
}
let bird = new Bird("小鸟",'啾啾');
bird.say() //animal--小鸟---啾啾
- 组合继承(通过调用父类构造,继承父类属性,然后通过实例作为子类原型)
function Animal(){
this.title="animal";
this.say = function(){
console.log(this.title + '--' + this.name +"---"+this.type)
}
}
// Animal.prototype.ask = "你吃了没"
function Ha(name,type){
Animal.call(this);
this.name = name;
this.type = type;
}
Ha.prototype = new Animal();
Ha.prototype.constructor = Ha; // constructor修复
let ha = new Ha('二哈','嗷呜嗷呜嗷呜');
ha.say() //animal--二哈---嗷呜嗷呜嗷呜
- 寄生组合继承(组合继承改进版,通过寄生的方式,创建一个没有实例方法的类,将实例作为子类的原型,相当与在组合原型继承的时候砍掉了父类的实例属性,这样在调用父类构造的时候,就不会初始化两次实例方法,避免组合继承的缺点。)
function Animal(){
this.title="animal";
this.say = function(){
console.log(this.title + '--' + this.name +"---"+this.type)
}
}
Animal.prototype.ask = "你吃了没"
function Lion(name,type){
Animal.call(this);
this.name = name;
this.type = type;
}
(function(){
var Super = function(){};
Super.prototype = Animal.prototype;
Lion.prototype = new Super();
})()
Lion.prototype.constructor = Lion; //构造函数修复
let lion = new Lion("狮子","吼吼吼");
lion.say(); //animal--狮子---吼吼吼
说明
继承方式有很多种,每一种都有自己的特点,比如构造函数继承
只能继承父类的实例属性和方法,不能继承原型属性/方法,在使用组合继承与寄生组合继承后需要修正子类构造函数的constructor
,为什么需要Lion.prototype.constructor = Lion
呢?可以参考《为什么要做A.prototype.constructor=A这样的修正?》,具体的不同点,可以自己动手敲一敲代码会逐渐发现的。