JavaScript继承学习总结(面试篇)

说继承之前先说几个概念。

原型和原型链

在JS中万物皆对象,对象有分函数对象(Function),普通对象(Object)。
任何对象都具有隐式原型属性__proto__,只有函数对象有显示原型属性prototype

原型链:原型链是针对原型对象的,在查找实例属性时,先在实例中查找,如果没有找到,接着再去原型上查找,如果还没有找到就去其父级原型上查找,一直向上直到找到或者属性不存在返回null。

作用域链:作用域链是针对变量的,先在自己的变量范围内查找,找不到再沿着作用域向上一直查找。

继承:通过某种方式让一个对象可以访问到另一个对象中的属性和方法,我们把这种方式称之为继承 ,继承的好处就是可以实现属性方法共用。

继承方式

  1. 原型链继承(将父类的实例作为子类的原型)
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--猫咪---喵喵
  1. 构造继承(使用父类的构造函数来增强子类实例,等于是复制父类的实例属性或方法给子类)
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---汪汪汪
  1. 实例继承(为父类实例添加新特性,作为子类实例返回)
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--猪猪---哼哼哼
  1. 拷贝继承(将父级可枚举方法以及属性拷贝至子级原型中)
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--小鸟---啾啾
  1. 组合继承(通过调用父类构造,继承父类属性,然后通过实例作为子类原型)
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--二哈---嗷呜嗷呜嗷呜
  1. 寄生组合继承(组合继承改进版,通过寄生的方式,创建一个没有实例方法的类,将实例作为子类的原型,相当与在组合原型继承的时候砍掉了父类的实例属性,这样在调用父类构造的时候,就不会初始化两次实例方法,避免组合继承的缺点。)
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这样的修正?》,具体的不同点,可以自己动手敲一敲代码会逐渐发现的。

你可能感兴趣的:(JavaScript继承学习总结(面试篇))