当前位置: 首页 > Web前端 > HTML

JavaScript继承总结

时间:2023-04-03 00:06:31 HTML

原型链在读取一个实例的属性时,如果找不到,就会去寻找与该对象关联的原型中的属性。如果找不到,就找原型的原型,找最顶层的until。如果让原型对象指向另一种类型的实例……有趣的事情发生了。即:Person.prototype=animal2鉴于上面的游戏规则生效,如果你试图引用Person构造的实例person1的一个属性:1).首先,它会在person1的内部属性中搜索;2).然后会在person1.__proto__(Person.prototype)中查找,Person.prototype其实就是animal2,也就是说在animal2;3中寻找属性p1)。如果还是没有找到animal2,此时程序也不会灰心,会继续在animal2.__proto__(Animal.prototype)...中查找,直到Object的原型对象查找轨迹:person1-->animal2-->动物。prototype-->Object.prototype的搜索轨迹看起来像一条长长的链条,由于prototype在这个游戏规则中起到了链接的作用,所以我们称这条instance和prototype链为prototype链。基础版图片扩展ImageJavaScriptInheritance继承就是复制。但是,默认情况下,JavaScript不会复制对象的属性。相反,JavaScript只是在两个对象之间建立关联,使一个对象可以通过委托访问另一个对象的属性和特性。函数,所以与其说是继承,不如说是委托更准确。——《你不知道的JavaScript》基于原型链与大多数其他语言不同,JavaScript是一个基于原型的对象系统,而不是基于类。基于原型的面向对象设计方法分为三种类型。拼接继承:这是一种将属性从一个对象直接复制到另一个对象的模式。被复制的原型通常称为mixin。ES6为这种模式提供了一个方便的工具Object.assign()。在ES6之前,一般是使用Underscore/Lodash提供的.extend(),或者jQuery中的$.extend()来实现。上面的对象组合例子使用了拼接继承的方法。原型代理:在JavaScript中,对象可能包含对原型的引用,称为代理。如果当前对象中不存在某个属性,则查找其代理原型。代理原型本身也会有自己的代理原型。这样就形成了一个原型链,在代理链中向上查找,直到找到属性,或者找到根代理Object.prototype。原型是这样的,通过使用new关键字创建实例和Constructor.prototype形成继承链。当然也可以使用Object.create()来达到同样的目的,也可以混合拼接继承,这样就可以将多个原型化为一个代理,也可以在对象实例化之后进行扩展创建。函数继承:在JavaScript中,任何函数都可以用来创建对象。如果函数既不是构造函数也不是类,则称为工厂函数。功能继承的工作原理是通过从工厂函数创建对象并将属性直接添加到该对象(使用拼接继承)来扩展对象。函数继承的概念最早是由DouglasCrockford提出的,但这种继承方式在JavaScript中早已存在。通过构造函数继承(经典继承)functionParent1(){this.name='parent1';}Parent1.prototype.say=function(){}functionChild1(){Parent1.call(this);this.type='孩子';}console.log(newChild1);这里主要是用call改变this的方向,通过call调用Parent,Parent中的this指的是Child1。有一个缺点。从打印结果可以看出Child没有say方法,所以这种child只能继承父类的实例属性和方法,不能继承原型属性/方法。注意构造函数属性。为了记录“哪个函数创建了临时对象”,new操作提前给“Child.prototype”添加了一个constructor属性:使用原型链实现继承函数Parent2(){this.name='parent2';this.play=[1,2,3];}functionChild2(){this.type='child2';}Child2.prototype=newParent2();console.log(newChild2);我们知道要共享一些属性,需要object.__proto__=parentobject's.prototype,但实际上我们不能直接操作__proto__,我们可以使用new来实现,所以Child2.prototype=newParent2();<=>Child2.prototype.__proto__=Parent2.prototype;这样,我们就可以借助新的语法糖实现原型链继承。缺点:给s1.play增加一个新的值,s2也变了。所以这就是原型链继承的缺点,因为s1.__pro__和s2.__pro__指向同一个地址,也就是父类Child2的原型。组合继承是指将原型链和构造函数结合起来,充分发挥两者优势的一种继承模式。其思想是利用原型链实现原型属性和方法的继承,通过借用构造函数实现实例属性的继承。这样通过在原型上定义方法来实现功能复用,并且可以保证每个实例都有自己的属性。初级版本函数Super(name){this.name=name;this.colors=["red","blue","green"];}Super.prototype.sayName=function(){alert(this.name);};functionSub(name,age){Super.call(this,name);//继承Super属性(第二次调用Sup构造函数)this.age=age;}//继承Super原型链方法(第一次调用Sup构造函数)注意后面需要修改,因为我们只想要方法,但是属性Sub.prototype=newSuper();Sub.prototype.constructor=Sub;//子原型。sayAge=function(){alert(this.age);};varinstance1=newSub("Luke",18);instance1.colors.push("black");alert(instance1.colors);//"红、蓝、绿、黑"instance1.sayName();//"Luke"instance1.sayAge()//18varinstance2=newSub("Jack",20);alert(instance2.colors);//"红、蓝、绿"instance2.sayName();//"Jack"instance2.sayAge()//20优化组合继承函数Super(name){this.name=name;this.colors=["red","blue","green"];}Super.prototype.sayName=function(){alert(this.name);};functionSub(name,age){Super.call(这,姓名);//继承Super属性this.age=age;}functionF(){}F.prototype=Super.prototype;Sub.prototype=newF();//继承Super原型链上的方法Sub.prototype.constructor=Sub;Sub.prototype.sayAge=function(){alert(this.age);};varinstance1=newSub("Luke",18);console.log(instance1)instance1.colors.push("black");alert(instance1.colors);//"红、蓝、绿、黑"instance1.sayName();//"Luke"instance1.sayAge()//18varinstance2=newSub("Jack",20);alert(instance2.colors);//"红、蓝、绿"instance2.sayName();//"Jack"instance2.sayAge()//20为什么要这样写?函数F(){}F.prototype=Super.prototype;Sub.prototype=newF();//继承Super原型链上的方法,而不是Sub.prototype=Super.prototype;下面的方法没有办法区分一个对象是直接由其子类实例化的还是由父类实例化的?下面是第一个不能判断instance1instanceofSub//trueinstance1instanceofSuper//true我们还有一个判断对象是否是类实例的方法,就是用constructor,我也分不清如果我在控制台打印如下内容:原型继承可以借助原型在现有对象的基础上创建新对象,同时不需要创建自定义类型。在myCreateObject()函数内部,先创建一个临时构造函数,然后把传入的对象当作this构造函数的原型,最终返回这个临时类型的一个新实例。functionmyCreateObject(o){functionF(){}F.prototype=o;//重写F的原型,将他指向传入的o,相当于继承自oreturnnewF();//返回F的实例对象}varperson={friends:["Van","Louis","Nick"]};varanotherPerson=myCreateObject(person);anotherPerson.friends.push("Rob");varyetAnotherPerson=myCreateObject(person);yetAnotherPerson.friends.push("Style");alert(person.friends);//"Van,Louis,Nick,Rob,Style"本质上,myCreateObject()对传入的对象进行浅拷贝,使用的子类指向传入的person对象object.create()方法规范了上述原型继承。上一篇有这个方法详解varperson={friends:["Van","Louis","Nick"]};varanotherPerson=Object.create(person);anotherPerson.friends.push("Rob");varyetAnotherPerson=Object.create(person);yetAnotherPerson.friends.push("Style");alert(person.friends);//"Van,Louis,Nick,Rob,Style"console.log(anotherPerson)缺点:原型链继承多个实例的引用类型属性指向同一个,存在被篡改的可能。不能传递参数。寄生继承(封装继承过程)创建一个只用于封装继承过程的函数。函数在内部以某种方式增强对象,最后返回对象。核心:在原型继承的基础上,增强对象,返回构造函数。函数的主要作用是在构造函数中添加属性和方法来增强功能。函数createAnother(原始){varclone=myCreateObject(原始);//通过调用myCreateObject()函数创建一个新对象,myCreateObject是任何可以返回对象的函数clone.sayHi=function(){//以某种方式增强对象alert("hi");};返回克隆;//返回这个对象}varperson={name:"Nicholas",friends:["Shelby","Court","Van"]};varanotherPerson=createAnother(person);另一个人.sayHi();//"hi"cons(同原型继承):原型链继承多个实例指向同一个点的引用类型属性,存在被篡改的可能。无法传递参数寄生组合继承(调用+寄生封装)寄生组合继承原理:使用借用构造函数(调用)继承父类声明的属性/方法this来继承父类原型声明的属性/方法班级。组合继承的缺点是会两次调用父类的构造函数,造成浪费。寄生组合继承可以解决这个问题。functioninheritPrototype(subType,superType){//原型继承:浅拷贝superType.prototype对象作为superType.prototype是新对象的原型//里面会有自己的_proto_pointer:prototype.\_\_proto\_\_=超级类型.原型;varprototype=Object.create(superType.prototype);//subType.prototype.\_\_proto\_\_=superType.prototype;subType.prototype=原型;//替换子类的原型Forthisprototypeprototype.constructor=subType;//修复原型构造函数}functionSuperType(name){this.name=name;this.colors=["red","blue","green"];}超类型。prototype.sayName=function(){alert(this.name);};functionSubType(name,age){SuperType.call(this,name);this.age=age;}//核心:因为是给父类的原型的拷贝,所以不包含父类的构造函数,不会调用两次父类的构造函数造成浪费inheritPrototype(SubType,SuperType);SubType.prototype.sayAge=function(){alert(this.age);}优缺点:这是一个完美的继承方式。Object.create可以用下面的函数代替functionmyCreateObject(o){functionF(){}F.prototype=o;//重写F的原型,指向传入的o,相当于继承自oreturnnewF();//new会将实例的__proto__指向F.prototype,达到和Object.create一样的效果}