6种继承的优点和缺点

一、原型链继承

Parent.prototype.grand = 'grand';

function Parent (name) {
     this.name = name || 'parent';
     this.color = ['red', 'orange', 'yellow'];
 }

 function Child () {

 }

 Child.prototype = new Parent();
 var child1 = new Child('child1');
 var child2 = new Child('child2');

缺点

1.引用值共享问题
引用值会被所有的实例(这里以两个为例,下面也将都是)共享,一个对象修改了原型属性,那么另一个的原型属性也会被修改

2.不能传参
在创建Child的实例时,不能向Parent传递参数;如果传递也不会有作用

6种继承的优点和缺点_第1张图片


二、借用构造函数(经典继承)

Parent.prototype.grand = 'grand';

function Parent (name) {
     this.name = name || 'parent';
     this.color = ['red', 'orange', 'yellow'];
 }

 function Child (name,) {
      Parent.call(this, name);
 }

 var child1 = new Child('child1');
 var child2 = new Child('child2');

优点

1.解决了引用值共享的问题

2.可以通过Child向Parent传参
6种继承的优点和缺点_第2张图片

缺点

1.多执行了一次函数 call

2.Parent上的原型不能被继承

6种继承的优点和缺点_第3张图片

注:为了证明Parent的原型上有grand这个属性值,我在控制台新建了一个parent对象


三、组合继承(伪经典继承)

将原型链继承和借用构造函数阶乘结合在一起

	Parent.prototype.grand = 'grand';

    function Parent(name) {
        this.name = name || 'parent';
        this.color = ['red', 'orange', 'yellow'];
    }


    function Child(name, age) {
        Parent.call(this, name);
        this.age = age;
    }

    Child.prototype = new Parent();
    Child.prototype.constructor = Child;

    var child1 = new Child('child1', 18);
    var child2 = new Child('child2', 19);

优点(结合了原型链继承和构造函数继承的优点)

1.Parent上的原型可以被继承

2.解决了引用值共享的问题

3.可以通过Child向Parent传参

6种继承的优点和缺点_第4张图片

缺点

函数多执行了一次call


四、原型式继承

    var car = {
        name : 'car',
        color : ['red', 'orange', 'yellow']
    }

    function object (o) {
        function F () {};
        F.prototype = o;
        return new F();
    }

    var car1 = object(car);
    var car2 = object(car);

重点:用一个函数包装一个对象,然后返回这个函数的调用,这个函数就变成了个可以随意增添属性的实例或对象。object.create()就是这个原理。

特点:类似于复制一个对象,用函数来包装。

缺点:1、所有实例都会继承原型上的属性。

2、无法实现复用。(新实例属性都是后面添加的)


五、寄生式继承

 function createObject (o) {
     var clone = Object.create(o);
     // var clone = object(o);
     clone.sayHi = function () {
         console.log('hi');
     }
     return clone;
 }

 var car = {
     name : 'car',
     color : ['red', 'orange', 'yellow']
 }

 var car1 = createObject(car);

重点:就是给原型式继承外面套了个壳子。

优点:没有创建自定义类型,因为只是套了个壳子返回对象(这个),这个函数顺理成章就成了创建的新对象。

缺点:没用到原型,无法复用。


六、寄生组合式继承

为了方便大家阅读,在这里重复一下组合继承的代码:

function Parent (name) {
this.name = name;
this.colors = [‘red’, ‘blue’, ‘green’];
}

Parent.prototype.getName = function () {
console.log(this.name)
}

function Child (name, age) {
Parent.call(this, name);
this.age = age;
}

Child.prototype = new Parent();

var child1 = new Child(‘kevin’, ‘18’);

console.log(child1)
组合继承最大的缺点是会调用两次父构造函数。

一次是设置子类型实例的原型的时候:

Child.prototype = new Parent();
一次在创建子类型实例的时候:

var child1 = new Child(‘kevin’, ‘18’);
回想下 new 的模拟实现,其实在这句中,我们会执行:

Parent.call(this, name);
在这里,我们又会调用了一次 Parent 构造函数。

所以,在这个例子中,如果我们打印 child1 对象,我们会发现 Child.prototype 和 child1 都有一个属性为colors,属性值为[‘red’, ‘blue’, ‘green’]。

那么我们该如何精益求精,避免这一次重复调用呢?

如果我们不使用 Child.prototype = new Parent() ,而是间接的让 Child.prototype 访问到 Parent.prototype 呢?

看看如何实现:

function Parent (name) {
this.name = name;
this.colors = [‘red’, ‘blue’, ‘green’];
}

Parent.prototype.getName = function () {
console.log(this.name)
}

function Child (name, age) {
Parent.call(this, name);
this.age = age;
}

// 关键的三步
var F = function () {};

F.prototype = Parent.prototype;

Child.prototype = new F();

var child1 = new Child(‘kevin’, ‘18’);

console.log(child1);
最后我们封装一下这个继承方法:

function object(o) {
function F() {}
F.prototype = o;
return new F();
}

function prototype(child, parent) {
var prototype = object(parent.prototype);
prototype.constructor = child;
child.prototype = prototype;
}

// 当我们使用的时候:
prototype(Child, Parent);

ps:建议去看下红宝石书

你可能感兴趣的:(js学习笔记)