javascript原型继承(第五篇)---几种继承方式的优缺点

在讲几种继承方式的优缺点之前,先给大家介绍一下在javascript中对各种属性的分类

在javascript中,数据分为简单数据和复杂数据,简单数据在对象里称为基本属性,而数组,函数在对象里称为引用属性(数组,函数其实都是对象)

对此不太清楚的,可以参考我之前的博客 javascript数据类型

下面具体介绍一下在对象里各种属性的分类

function Fun(){
    // 私有属性
    var val = 1;        // 私有基本属性
    var arr = [1];      // 私有引用属性
    function fun(){}    // 私有函数(引用属性)

    // 实例属性
    this.val = 1;               // 实例基本属性
    this.arr = [1];             // 实例引用属性
    this.fun = function(){};    // 实例函数(引用属性)
}

// 原型属性
Fun.prototype.val = 1;              // 原型基本属性
Fun.prototype.arr = [1];            // 原型引用属性
Fun.prototype.fun = function(){};   // 原型函数(引用属性)

在看此篇博客之前,需要先看一下我的上一篇博客 javascript原型继承(第四篇)—几种继承方式,且要看懂里面的内容。

在看懂上一篇的基础上,再来看这篇,便没有什么难度了,而我的本意也就是用这篇写一下上一篇里各个继承方式的优缺点。

一、原型链继承
function Super(){
    this.val = 1;
    this.arr = [1];
}
function Sub(){
    // ...
}
Sub.prototype = new Super();    // 核心
Sub.prototype.constructor = Sub;

var sub1 = new Sub();
var sub2 = new Sub();
sub1.val = 2;
sub1.arr.push(2);
alert(sub1.val);    // 2
alert(sub2.val);    // 1

alert(sub1.arr);    // 1, 2
alert(sub2.arr);    // 1, 2

优点:

1.简单,易于实现

缺点:

1.修改sub1.arr后sub2.arr也变了,因为来自原型对象的引用属性是所有实例共享的。
(原因:执行sub1.arr.push(2);先对sub1进行属性查找,找遍了实例属性(在本例中没有实例属性),没找到,就开始顺着原型链向上找,拿到了sub1的原型对象,一搜身,发现有arr属性。于是给arr末尾插入了2,所以sub2.arr也变了)

2.创建子类实例时,无法向父类构造函数传参

二、构造函数继承

原型链很简单,可是存在2个致命缺点简直不能用。于是,便有了这个构造函数继承,

function Super(val){
    this.val = val;
    this.arr = [1];

    this.fun = function(){
        // ...
    }
}
function Sub(val){
    Super.call(this, arguments);   // 核心
    // ...
}

var sub1 = new Sub(1);
var sub2 = new Sub(2);
sub1.arr.push(2);
alert(sub1.val);    // 1
alert(sub2.val);    // 2

alert(sub1.arr);    // 1, 2
alert(sub2.arr);    // 1

alert(sub1.fun === sub2.fun);   // false,因为sub1和sub2各自拥有fun,它们的内存地址是不同的

优点:

1.解决了子类实例共享父类引用属性的问题
2.创建子类实例时,可以向父类构造函数传参

缺点:

1.无法实现函数复用,每个子类实例都持有一个新的fun函数,如果实例太多,会造成内存大量被占用

三、组合继承

目前,我们的构造函数继承仍旧有问题,所以便产生了组合继承

function Super(){
    // 只在此处声明基本属性和引用属性
    this.val = 1;
    this.arr = [1];
}
//  在此处声明函数
Super.prototype.fun = function(){};
//Super.prototype.fun3...
function Sub(){
    Super.call(this);   // 核心
    // ...
}
Sub.prototype = new Super();    // 核心

var sub1 = new Sub(1);
var sub2 = new Sub(2);
sub1.val = 2;
sub1.arr.push(2);
alert(sub1.val);    // 2
alert(sub2.val);    // 1

alert(sub1.arr);    // 1, 2
alert(sub2.arr);    // 1
alert(sub1.fun === sub2.fun); //true 

优点:

1.不存在引用属性共享问题
2.创建子类实例时,可以向父类构造函数传参
3.函数可复用

缺点:(一点小瑕疵,但仍旧不影响它是现在最常用的继承方式)

1.子类原型上有一份多余的父类实例属性,因为父类构造函数被调用了两次,生成了两份,而子类实例上的那一份屏蔽了子类原型上的。

最后,javascript原型继承到此就告一段落了,其实还有在js中还有很多继承方式,不过我觉得了解并掌握了上述三种继承方式,已经足够纵横天下了。


参考了相关博客梦烬–重新理解js继承方式

你可能感兴趣的:(web前端,javascript)