当前位置: 首页 > 科技观察

JavaScript深入继承的各种方法和优缺点

时间:2023-03-15 13:19:07 科技观察

在上一篇文章中写过,讲解JavaScript的各种继承方法和优缺点。不过注意:这篇文章更像是一个笔记,哎,让我再次感叹:《JavaScript高级程序设计》写的真好!1、原型链继承functionParent(){this.name='kevin';}Parent.prototype.getName=function(){console.log(this.name);}functionChild(){}Child.prototype=newParent();varchild1=newChild();console.log(child1.getName())//kevin问题:1.引用类型的属性是所有实例共享的,例如:functionParent(){this.names=['kevin','daisy'];}functionChild(){}Child.prototype=newParent();varchild1=newChild();child1.names.push('yayu');console.log(child1.names);//["kevin","daisy","yayu"]varchild2=newChild();console.log(child2.names);//["kevin","daisy","yayu"]2。创建Child实例时,不能给Parent传递参数2.借用构造函数(经典继承)functionParent(){this.names=['kevin','daisy'];}functionChild(){Parent.call(this);}varchild1=newChild();child1.names.push('yayu');console.log(child1.names);//["kevin","daisy","yayu"]varchild2=newChild();console.log(child2.names);//["kevin","daisy"]优点:1.避免所有实例共享引用类型的属性2.可以在Chi中使用在ld中给Parent传递参数例如:functionParent(name){this.name=name;}functionChild(name){Parent.call(this,name);}varchild1=newChild('kevin');console.log(child1.name);//kevinvarchild2=newChild('daisy');console.log(child2.name);//daisy缺点:方法定义在构造函数中,每次创建实例都会重新创建方法3.组合继承原型链继承和经典继承是两把利剑。functionParent(name){this.name=name;this.colors=['red','blue','green'];}Parent.prototype.getName=function(){console.log(this.name)}functionChild(名字,年龄){Parent.call(this,name);this.age=age;}Child.prototype=newParent();ChildChild.prototype.constructor=Child;varchild1=newChild('kevin','18');child1.colors.push('black');console.log(child1.name);//kevinconsole.log(child1.age);//18console.log(child1.colors);//["red","blue","green","black"]varchild2=newChild('daisy','20');console.log(child2.name);//daisyconsole.log(child2.age);//20console.log(child2.colors);//["red","blue","green"]优点:结合了原型链继承和构造函数的优点,是JavaScript中最常用的继承模式。4.原型继承functioncreateObj(o){functionF(){}F.prototype=o;returnnewF();}是ES5Object.create的模拟实现,将传入的对象作为创建对象的原型。缺点:包含引用类型的属性值会一直共享对应的值,这和原型链继承是一样的。varperson={name:'kevin',friends:['daisy','kelly']}varperson1=createObj(person);varperson2=createObj(person);person1.name='person1';console.log(person2.name);//kevinperson1.firends.push('taylor');console.log(person2.friends);//["daisy","kelly","taylor"]注意:修改person1.name,person2的值.name的值没有变,不是因为person1和person2有独立的name值,而是因为person1.name='person1',给person1增加了一个name值,并没有修改原型上的name值。5.寄生继承创建了一个函数,只是用来封装继承过程。这个函数在内部以某种形式增强对象,最后返回对象。functioncreateObj(o){varclone=Object.create(o);clone.sayName=function(){console.log('hi');}returnclone;}缺点:和借用构造函数模式一样,每次创建一个对象,它将被创建一次性方法。6.寄生组合继承为了方便大家阅读,这里放上组合继承的代码:functionParent(name){this.name=name;this.colors=['red','blue','green'];}Parent.prototype.getName=function(){console.log(this.name)}functionChild(name,age){Parent.call(this,name);this.age=age;}Child.prototype=新父母();varchild1=newChild('凯文','18');console.log(child1)组合继承最大的缺点是父构造函数会被调用两次。一次在设置子类型实例的原型时:Child.prototype=newParent();创建子类型实例时一次:varchild1=newChild('kevin','18');在这句话中,我们将执行:Parent.call(this,name);在这里,我们将再次调用Parent构造函数。所以,在这个例子中,如果我们打印child1对象,我们会发现Child.prototype和child1都有一个名为colors的属性,其值为['red','blue','green']。那么这次如何才能精益求精,避免重复调用呢?如果我们不使用Child.prototype=newParent(),而是让Child.prototype间接访问Parent.prototype会怎么样?看看这是如何完成的:functionParent(name){this.name=name;this.colors=['red','blue','green'];}Parent.prototype.getName=function(){console.log(this.name)}functionChild(name,age){Parent.call(this,name);this.age=age;}//关键三步varF=function(){};F.prototype=Parent.prototype;Child.prototype=newF();varchild1=newChild('kevin','18');console.log(child1);最后我们封装这个继承方法:functionobject(o){functionF(){}F.prototype=o;returnnewF();}functionprototype(child,parent){varprototype=object(parent.prototype);prototype.constructor=child;child.prototype=prototype;}//当我们使用:prototype(Child,Parent);《JavaScript高级程序设计》中引用的寄生组合继承的赞誉是:该方法的高效体现在只调用Parent构造函数一次,从而避免在Parent.prototype上创建不必要的冗余属性。同时,原型链保持不变;所以instanceof和isPrototypeOf是可以正常使用的。开发者普遍认为寄生组合继承是引用类型最理想的继承范式。