深入理解javascript的5种继承方式

    xiaoxiao2025-03-16  15

    1.构造函数式

    function Super(){ this.colors = ['c','a','b']; this.print = function(){ console.log(this.colors); } } function Sub(){ Super.call(this); } var instance1 = new Sub(); instance1.colors.push('v'); console.log(instance1.colors); //c,a,b,v var instance2 = new Sub(); console.log(instance2.colors);//c a b

    我们发现,构造函数方式就是假借call和apply方法,把父类的构造函数嫁接到子类身上,子类new出一个实例的时候,就把父类的构造函数的内容copy到实例中了。

    所以每个实例都有自己的一份拷贝。

    对于基本属性而言,非常棒,每个实例之间不共享属性,立刻拥有自己的私有属性了。 对于方法属性而言,不太好。我原本希望实现1个函数在内存存储,最好没有拷贝,却能被父类和子类一起来共享。

    实现了属性的私有,没有实现方法的共享。

    2.原型链式

    function Super(){ this.colors = ['c','a','b']; this.print = function(){ console.log(this.colors); } } function Sub(){ } Sub.prototype = new Super(); var instance1 = new Sub(); instance1.colors.push('v'); console.log(instance1.colors); //c,a,b,v var instance2 = new Sub(); console.log(instance2.colors);//c a b v

    原型链式借用了js的对象的一个特性:对象去寻找自己的属性的时候,先在自己的当前对象上寻找,如果没有,就去自己的原型链上寻找,一直顺着原型链向上寻找直到找到null。

    那么,对于一个方法属性而言,这太完美了。如果我没有重载方法,我就去利用父类的方法的话,我不需要复制方法的拷贝,这不就节省内存了吗?!

    对于一个基本属性而言,这太尴尬了,子类修改了属性的值,父类和所有其他子类的值就全部被修改了。因为大家伙使用的是相同指针指向的堆上同一个值。

    方法和属性全都被共享了。

    3.组合式

    function Super(){ this.colors = ['c','a','b']; } Super.prototype.print = function(){ console.log(this.colors); }; function Sub(){ Super.call(this); } Sub.prototype = new Super(); Sub.prototype.constructor = Sub; var instance1 = new Sub(); instance1.colors.push('v'); console.log(instance1.colors); //a b c v var instance2 = new Sub(); console.log(instance2.colors); //a b c

    组合式继承采用了原型链式继承和构造函数式继承结合的办法。

    我们把(我们希望子类有一份拷贝的)基本属性放在父类的构造函数里。 我们把希望共享的函数方法放在父类的原型上。

    实现了函数的共享,也实现了属性的私有。

    4.Object.create式(寄生式)

    var sup = { colors : ['c','a','b'] } var instance1 = Object.create(sup); instance1.colors.push('v'); console.log(instance1.colors); //a b c v var instance2 = Object.create(sup); console.log(instance2.colors); //a b c v

    这里使用了Object.create。这是在ES5中才有的。IE8及以下只能来模拟Object.create。

    function create(base){ function c(){}; c.prototype = base ; return new c; }

    原来是一种变了花样的原型继承呀。

    所以它有原型继承的所有缺点 :基本属性不能私有

    还有所有优点:方法属性可以共享

    不同点就是:我们不需要去创建函数来表示一个类(构造函数),通过类(构造函数)去创建一个对象。object.create里面就帮我们创建类(构造函数)了。我们在外面看,好像实现了object之间的直接继承。

    5.寄生组合式

    function Super(){ this.colors = ['c','a','b']; } Super.prototype.print = function(){ console.log(this.colors); }; function Sub(){ Super.call(this); } var proto = Object.create(Super.prototype); proto.constructor = Sub; Sub.prototype = proto ; var instance1 = new Sub(); instance1.colors.push('v'); console.log(instance1.colors); //a b c v var instance2 = new Sub(); console.log(instance2.colors);//a b c

    组合式都这么完美了,为什么还要出一个寄生式呢??

    组合式并不完美啊。有一个很大的问题。基本属性在子类对象里出现了两次!!

    在组合方式下,截图了instance1对象。

    哇,真的啊!在原型里有一个colors数组。在对象本身也有一个colors数组。 Sub.prototype = new Super();这一句把super的color数组共享到sub的原型上了。 Super.call(Sub)这一句把super的color数组复制到sub上了。

    好吧,一个对象上竟然有两份colors,这很不满意!掀桌。

    大大们就发明了寄生组合式继承方式。

    让我们来看一下,这个方式下的instance1变量是什么样子的呢?

    哈哈,colors只有一份copy,没有在原型里被共享哦。

    让我们取出关键代码来分析一下这是为什么呐。

    function Sub(){ Super.call(this); } var proto = Object.create(Super.prototype); proto.constructor = Sub; Sub.prototype = proto ;

    Super.call(this);这个实现了对colors的拷贝。 var proto = Object.create(Super.prototype);这句非常关键,我们只把super原型上的内容放在proto的原型链上了。 proto.constructor = Sub;Sub.prototype = proto ;实现了原型和构造器的互相指引。

    与组合式不通的就是寄生组合式更加关注了父类的基本类型不能被共享到原型链上,变成误用的代码,浪费内存。

    转载请注明原文地址: https://ju.6miu.com/read-1297079.html
    最新回复(0)