手机版

JavaScript面向对象原型和继承

时间:2021-10-09 来源:互联网 编辑:宝哥软件园 浏览:

1.前言本文翻译自Scott Allen Prototypes和JavaScript中的继承,也就是微软。本文详细分析和阐述了什么是prototype,为什么可以通过Prototype实现继承,这是理解JS OO最好的作品之一。希望大家对差的翻译进行修改补充。第二,JavaScript中的面向对象语言不同于其他语言,学习之前最好忘记自己熟悉的面向对象概念。JS中的OO更强大,可以说也更灵活。1.类和对象JS传统上是面向对象的语言。属性和行为组合成一个对象。比如JS中的array就是一个由属性和方法组成的对象(比如push、reverse、pop等)。).复制代码如下: var myArray=[1,2];my array . push(3);my array . reverse();my array . pop();var length=myArray.length问题是:这些方法(比如推)从何而来?一些静态语言(如JAVA)使用类来定义对象的结构。但是JS是一种没有“类”(无类)的语言,没有一个叫做“Array”的类为每个数组定义这些方法来继承。因为JS是动态的,所以我们可以根据需要向对象添加方法。例如,下面的代码定义了一个对象,该对象表示二维空间中的坐标,并且内部有一个add方法。复制的代码如下:VARPoint={x: 10,y: 5,add 3360 function(other point){ this。x=otherpoint.xthis . y=other point . y;} };我们希望每个点对象都有一个add方法。我们也希望所有的poin对象共享一个add方法,而不是将add方法添加到所有的点对象。这就是为什么原型应该在舞台上。2.原型JS中的每个对象都有一个隐式属性(状态)——,它引用另一个对象,称为对象的原型。当然,我们上面创建的数组和点也包含它们自己的原型引用。原型引用是隐式的,但是它是由ECMAScript实现的,ECMAScript允许我们通过使用对象的_proto_(在Chrome中)属性来获取它。从概念上,我们可以认为对象与原型的关系如下图所示:prototype1

作为开发人员,我们将使用Object.getPrototypeOf函数而不是_proto_ property来查看对象的原型引用。在撰写本文时,函数Object.getPrototypeOf已经在Chrome、firefox和IE9中得到支持。未来会有更多的浏览器支持这个功能,这已经是ECMAScript标准之一。我们可以使用下面的代码来证明myArray和我们之前创建的点对象确实引用了两个不同的原型对象。复制代码如下:对象。getPrototypeof(点)!=object . getprototypeof(my array);在文章的下一部分,我还将使用_proto_主要是因为_proto_在图表和句子中更直观。但是请记住,这不是规范的,Object.getPrototypeOf是获取对象原型的推荐方法。2.1是什么让原型如此特别?我们已经知道数组的推送方法来自myArray的原型对象。图2是Chrome中的截图。我们调用Object.getPrototypeOf方法来获取myArray的原型对象。ff852808_img002(en-us,MSDN_10)

图2注意到myArray的原型对象包含许多方法,例如push、pop和reverse,我们在开始的代码中使用了这些方法。原型对象是push方法的唯一拥有者,但是这个方法是如何通过myArray调用的呢?复制代码如下: myarray . push(3);要理解它是如何实现的,第一步是要认识到Protytype一点也不特殊。原型只是一些物体。我们可以向这些对象添加方法和属性,就像任何其他JS对象一样。同时,原型也是一个特殊的对象。Prototype之所以特别,是因为以下规则:当我们通知JS我们要调用对象上的push方法或读取属性时,运行库首先会查找对象本身的方法或属性。如果解释器没有找到方法(或属性),它将跟随_proto_ reference来找到对象原型中的每个成员。当我们在myArray中调用push方法时,JS并没有在myArray对象中找到push,而是在myArray的原型对象中找到push,也就是所谓的push方法(图3)。ff852808_img003(en-us,MSDN_10)

图3我描述的行为本质上是对象本身继承了其原型中的所有方法和属性。我们不需要在JS中使用类来实现这种继承关系。也就是说,JS对象从其原型继承特性。图3还告诉我们,每个数组对象都可以维护自己的状态和成员。如果我们需要myArray的长度属性,JS会从myArray中找到长度的值,而不是在原型中寻找。我们可以使用这个特性来“覆盖”一个方法,也就是把要被覆盖的方法(比如push)放到myArray自己的对象中。这样做可以有效地在原型中隐藏推送方法。3.JS中共享原型prototype的真正魔力在于多个对象可以引用同一个Prototype对象。例如,我们创建两个数组:复制代码如下: var myArray=[1,2];var yourArray=[4,5,6];这两个数组将共享同一个原型对象,下面的代码将返回true。复制代码如下: object . getprototypeof(my array)==object . getprototypeof(your array);如果我们在两个数组中调用push方法,JS将在它们的公共原型中调用push。ff852808_img004(en-us,MSDN_10)

原型对象在JS中给了我们这个继承的特性,并且它们也允许我们共享方法的实现。原型也是连锁的。换句话说,原型是一个对象,所以原型对象也可以有对其他原型对象的引用。从图2可以看出,prototo的_proto_ attribute是一个非空值,指向另一个原型。当JS开始寻找成员变量时,比如push方法,它会沿着这些原型引用对每个对象进行null检查,直到找到这个对象或者到达链的末端。这种链式模式增加了JS中继承和共享的灵活性。接下来,你可能会问:如何设置自定义对象的原型引用?比如我们之前创建的对象点,如何给原型对象添加一个add方法,让所有的点对象都能继承?在回答这个问题之前,我们先了解一下JS中的函数。4.function函数也是JS中的一个对象。JS中有很多重要的特性,我们在本文中无法一一列举。然而,将一个函数赋给一个变量或者将一个函数作为另一个函数的参数是当今JS编程中非常基本的方式。我们需要注意的是,因为函数是一个对象,所以它有方法、属性和对原型对象的引用。让我们讨论以下代码的含义:按如下方式复制代码://这将返回true :类型的(array)==' function '//并且这将返回3360对象. getprototype类型的(array)。===object . getprototypeof(function(){ })//还有这个,too: Array.prototype!=null第一行代码证明Array是JS中的一个函数。稍后我们将看到如何调用Array函数来创建一个新的数组对象。第二行代码证明了array对象和function对象引用了同一个原型,就像我们之前看到的,所有的Array对象共享一个原型。最后一行证明Array函数有一个原型属性。切勿将此原型属性与_ prototype _属性混淆。它们用于不同的目的,指向不同的对象。复制代码如下://true array . prototype==object . getprototypeof(my array)//同样true array . prototype==object . getprototypeof(your array);我们用新知识重新绘制了之前的图片:ff852808_img005(en-us,MSDN_10)

图5现在我们要创建一个数组对象。一种方法是复制代码如下://创建一个新的空对象var o={ };//从与数组对象相同的原型继承o. _ _ proto _ _=Array.prototype//现在我们可以调用任何数组方法.o . push(3);虽然上面的代码看起来不错,但问题是每个JS环境都支持对象的_proto_ property。幸运的是,JS有一个内置的标准机制来创建新的对象并设置对象的_proto_ property,这被称为“new”运算符。复制代码如下: var o=new Array();o . push(3);“new”操作符在JS中有三个重要的任务:首先,它创建一个新的空对象。然后,它设置这个新对象的_proto_ property指向调用函数的原型属性。最后,执行调用函数并将“this”指针指向新对象。如果我们展开上面两行代码,会得到如下代码:复制代码如下: var o={ };o . _ _ proto _ _ _=array . prototype;array . call(o);o . push(3);函数的“调用”方法允许您调用函数,并指定函数中的“this”指向传入的新对象。当然,我们也想通过上面的方法创建自己的对象,实现对象的继承。这个函数就是大家熟知的——构造函数。5.构造函数构造函数是一个普通的JS函数对象,有两个唯一的标识:1。第一个字母大写(易于识别)。2.使用新的运算符连接来构造新的对象。数组是一个构造函数,——。数组函数用new连接,第一个字母大写。JS中的Array函数是内置的,但是任何人都可以创建自己的构造函数。事实上,我们终于到了为点对象创建构造函数的时候了。复制的代码如下:VARPoint=function (x,y) {this。x=xthis.y=ythis . add=function(other point){ this . x=other point . x;this . y=other point . y;} } var p1=new Point(3,4);var p2=新点(8,6);P1 . add(p2);在上面的代码中,我们使用新的运算符和点函数来构造点对象。在内存中,您可以想到如图6所示的最终结果。ff852808_img006(en-us,MSDN_10)

图6现在的问题是add方法存在于每个点对象中。鉴于我们对原型的理解,将add方法添加到Point.prototype是一个更好的选择(不需要将add方法的代码复制到每个对象中)。为了实现这一点,我们需要对Point.prototype对象进行一些修改。复制的代码如下:VARPoint=function (x,y) {this。x=xthis.y=y} point . prototype . add=function(other point){ this . x=other point . x;this . y=other point . y;} var p1=新点(3,4);var p2=新点(8,6);P1 . add(p2);好吧。我们已经用原型实现了JS中的继承!ff852808_img007(en-us,MSDN_10)

6.总结希望这篇文章能帮你拨开原型的迷雾。当然,这只是对强大而灵活的原型的介绍。关于原型的更多知识,希望读者自己去探索发现。

版权声明:JavaScript面向对象原型和继承是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。