JavaScript七种继承方式

279 阅读4分钟

1.借用构造函数继承

方法:在子类型构造函数的内部调用父类型构造函数,通过使用apply()或call()方法指向this和传入参数使用

弊端:

1.创建出来的实例无法调用父类原型上的方法

2.原型链指向也不对,没有指向父类

function girlFriend(age){
    this.girls = ['chen','wang','zhu'];
    this.age = age;
}
girlFriend.prototype.sayLove = function(){
    console.log("i love you");
}
function Person(){
    girlFriend.call(this,20);
}
var wang = new Person();
var zhu = new Person();
wang.girls.push('zhang');
console.log(wang.girls);    // (4) ["chen", "wang", "zhu", "zhang"]
console.log(zhu.girls);        // (3) ["chen", "wang", "zhu"]
console.log(wang.sayLove());	// undefined (爱都是不存在的)
console.log(zhu.sayLove());	// undefined (爱都是不存在的)

2.原型链继承

方法:复写子对象的原型,new父对象的实例给它

弊端:

1.子类的构造器指向父类

2.创建多个实例的话,所有实例的原型都会指向一个地址,任意改变一个也就是改变所有

// 原型链继承
function SuperType(){
    this.name = 'super';
    this.girlFriends = ["xiaoli","xiaowang"];
}
SuperType.prototype.sayName = function(){
    console.log(this.name);
}
function SubType(){
    this.age = 20;
}
// 创建SuperType的实例赋给SubType的原型
// 实现继承的本质是重写原型对象,代之以一个新类型的实例
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function(){
    console.log(this.age);
}
var sub1 = new SubType();
sub1.sayName();        // super
sub1.sayAge();        // 20
// 现在sub.constructor指向SuperType
// 是因为原来SubType的constructor被重写的缘故
console.log(sub1.constructor);    // 输出的SuperType函数

// 原型链的问题
// 通过以下代码,我们会发现SubType的所有实例都会共享这girlFriends属性
// 由此可知,用原型链继承方式继承的构造函数,它不管创建了多少个实例,都会共享父类构造函数中的引用值var sub2 = new SubType();
sub1.girlFriends.push("xiaochen");
console.log(sub2.girlFriends);    // (3) ["xiaoli", "xiaowang", "xiaochen"]

3.组合继承

方法:结合1,2两种方式,原型链继承方式继承构造函数的属性,借用构造函数继承方式继承构造函数原型上的方法

弊端:

1.子类创建的实例的构造器指向父类,需要手动指定

// js中最常用的继承方式--组合继承
function Super(name){
    this.name = name;
    this.colors = ["red","blue","green"];
}
Super.prototype.sayName = function(){
    console.log(this.name);
}
function Sub(name){
    // 继承了属性
    Super.call(this,name);
}
// 继承了方法
Sub.prototype = new Super();
var sub1 = new Sub("Nick");
sub1.colors.push("black");
console.log(sub1.colors);    // (4) ["red", "blue", "green", "black"]
sub1.sayName();        		// Nick

var sub2 = new Sub("Lucy");
console.log(sub2.colors);    // (3) ["red", "blue", "green"]
sub2.sayName();        		// Lucy

// 返回的都是true
console.log(sub1 instanceof Object);
console.log(sub1 instanceof Super);
console.log(sub1 instanceof Sub);

console.log(Object.prototype.isPrototypeOf(sub2));
console.log(Super.prototype.isPrototypeOf(sub2));
console.log(Sub.prototype.isPrototypeOf(sub2));

4.原型式继承

方法:借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型

弊端:同原型链继承方式,毕竟就差一个字嘛,如下

1.子类的构造器指向不对

2.创建多个实例的话,所有实例的原型都会指向一个地址,任意改变一个也就是改变所有

// 此object方法同es5中的Object.create()方法
function object(o){
    function F(){};
    F.prototype = o;
    return new F();
}

// 在object()函数的内部,先创建了一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例
var person = {
    name:"Nick",
    friends:["xiaowang","xiaochen"]
};
var person1 = object(person);
person1.name = "Mike";
person1.friends.push("xiaozhang");

var person2 = object(person);
person2.name = "lifei";
person2.friends.push("xiaoli");
console.log("person1:" + person1.name);
console.log("person2:" + person2.name)
console.log("person1 friends:" + person1.friends);
console.log("person2 friends:" + person2.friends);
console.log("all friends:" + person.friends);

5.寄生式继承

方法:创建函数,传入父对象,用父对象作为Object.create()的参数创建对象,再返回此对象

弊端:同原型链继承方式,毕竟就差一个字嘛,如下

1.子类的构造器指向不对

2.创建多个实例的话,所有实例的原型都会指向一个地址,任意改变一个也就是改变所有

function createAnother(original){
        var clone = Object.create(original);
        clone.sayHi = function(){
            console.log('hi');
        }
        return clone;
}

var person = {
        name:"Nick",
        friends:["xiaowang","xiaochen"]
};

var person1 = createAnother(person);
var person2 = createAnother(person);
person1.sayHi();
person1.friends.push("xiaoyu");
console.log(person1.friends);	// ["xiaowang", "xiaochen", "xiaoyu"]
console.log(person2.friends);	// ["xiaowang", "xiaochen", "xiaoyu"]

6.寄生组合式继承

方法:通过构造函数来继承属性,通过原型式继承来继承方法

弊端:暂无

function inheritPrototype(sub,sup){
    var prototype = Object.create(sup.prototype);
    prototype.constructor = sub;
    sub.prototype = prototype;
}

function Super(name){
    this.name = name;
    this.colors = ["red","blue","yellow"];
}
Super.prototype.sayName = function(){
    console.log(this.name);
}
function Sub(name){
    Super.call(this,name);
}
// 继承
inheritPrototype(Sub,Super);
// 创建
let instance1 = new Sub("Bob");
let instance2 = new Sub("Nick");
instance1.colors.push("black");
console.log(instance1.clors);	// ["red", "blue", "yellow", "black"]
console.log(instance2.clors);	// ["red", "blue", "yellow"]

7.ES6继承方式

方法:通过es6新增的class和extend

弊端:无

class Father {
  constructor(age) {
    this.name = '张三';
    this.age = age;
  }
  hobby() {
    console.log('喜欢高尔夫');
  }
}

class Son extends Father {
  constructor(age) {
    // 通过 extends + super 继承
    super(age); // 可以传递参数到父类
    this.height = '178cm';
  }
  hobby() {
    console.log('喜欢篮球');
    super.hobby(); // 也可以继承方法
  }
}
const xiaozhang = new Son(50);
console.log(xiaozhang.name); // 张三
console.log(xiaozhang.age); // 50
xiaozhang.hobby();