手机版

JavaScript中原型和继承的详细说明(图形)

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

请忘记之前学过的所有面向对象的知识。在这里,我们只需要考虑赛车的情况。是的,是赛车。

最近在看法国热门赛事勒芒24小时耐力赛。最快的车叫勒芒原型车。虽然这些车是由“奥迪”或“标致”制造商制造的,但它们不是你在街上或高速公路上看到的那种车。它们是专门为参加高速耐力赛而制造的。

制造商在这些原型的研究、开发、设计和制造上投入了大量资金,工程师们总是试图将这个项目发挥到极致。他们对合金、生物燃料、制动技术、复合成分和轮胎的安全特性进行了各种实验。随着时间的推移,这些实验中的一些技术得到了反复改进,进而进入了车辆的主流产品线。你驾驶的车辆的一些技术有可能会在赛车原型上首次亮相。

你也可以说这些主流车辆继承了赛车的技术原型。

到目前为止,我们已经有了在JavaScript中讨论原型和继承的基础。虽然它与您在C、Java或C#中知道的经典继承模式不同,但它同样强大,并且可能更加灵活。

 有关对象和类

JavaScript充满了对象,是指传统意义上的对象,即“包含状态和行为的单一实体”。例如,JavaScript中的数组是一个对象,它有几个值以及推、反和弹出方法。

var myArray=[1,2];my array . push(3);my array . reverse();my array . pop();var length=myArray.length现在的问题是,push从何而来?前面提到的静态语言都是用“类语法”来定义对象的结构,但是JavaScript是一种没有“类语法”的语言,所以不可能用Array“类”的语法来定义每个Array对象。因为JavaScript是一种动态语言,我们可以根据需要在对象上放置方法。例如,下面的代码定义了一个用于表示二维空间中点的点对象,还定义了一个add方法。

var point={ x : 10,y : 5,add : function(other point){ this . x=other point . x;this . y=other point . y;}};但是上述方法的扩展性不好。我们需要确保每个点对象都包含一个add方法,我们也希望所有的点对象共享同一个add方法的实现,而不是手动将这个方法添加到每个点对象中。这就是原型发挥作用的地方。

 有关原型

在JavaScript中,每个对象都保持隐藏状态——,这是对另一个对象的引用,也称为prototype。我们之前创建的数组引用了一个原型对象,我们自己创建的点对象也是如此。据说原型引用是隐藏的,但是也有ECMAScript(JavaScript的JavaScript名)的实现可以通过对象的__proto__属性访问这个原型引用(比如Google浏览器)。从概念上讲,我们可以将对象视为类似于图1所示的对象——原型的关系。

图1

展望未来,开发人员将能够使用Object.getPrototypeOf函数而不是__proto__属性来获取对象原型的引用。在撰写本文时,Object.getPrototypeOf函数可以在Google Chrome、FIrefox和IE9浏览器中使用。未来会有更多的浏览器实现这个功能,因为它已经是ECMAScript标准的一部分。我们可以使用下面的代码来证明我们创建的myArray和Point对象引用了两个不同的原型对象。

Object.getPrototypeOf(点)!=object . getprototypeof(my array);在本文的剩余部分,我将交替使用__proto__和Object.getPrototypeOf函数,主要是因为__proto__在图形和句子中更容易识别。请记住它(_proto_)不是标准的,Object.getPrototypeOf函数是查看对象原型的推荐方法。

是什么让原型如此特别?

这个问题我们还没有回答:数组中的push方法是从哪里来的?答案是:它来自myArray原型对象。图2是Chrome浏览器中脚本调试器的截图。我们已经调用了Object.getPrototypeOf方法来查看myArray的原型对象。

图2

请注意,myArray的原型对象中有许多方法,包括代码示例中调用的push、pop和reverse方法。因此,push方法包含在原型对象中,但是如何引用myArray方法呢?

my array . push(3);了解其工作原理的第一步是认识到原型并不特殊。原型只是普通的物体。您可以向原型添加方法和属性,并像对待其他JavaScript对象一样对待它们。然而套用乔治奥威尔小说《动物农场》中的“猪”来说,所有的对象都应该是平等的,但有些对象(遵守规则的人)比其他人更平等。

JavaScript中的原型对象非常特殊,因为它们遵循以下规则。当我们告诉JavaScript我们想要调用一个对象的push方法或者读取一个对象的X属性时,运行库将首先寻找对象本身。如果运行库找不到它想要的东西,它将遵循__proto__引用和对象原型来查找成员。当我们调用myArray的push方法时,JavaScript没有在myArray对象上找到push方法,而是在myArray的原型对象上找到了,所以JavaScript调用了这个方法(见图3)。

图3

上述行为意味着对象本身继承原型上的任何方法或属性。在JavaScript中,不需要使用类语法就可以实现继承。就像从赛车原型继承相应技术的汽车一样,JavaScript对象也可以从原型对象继承功能特性。

图3还显示了每个数组对象也可以维护自己的状态和成员。在请求myArray的长度属性的情况下,JavaScript会获取myArray中长度属性的值,而不是读取原型中对应的值。我们可以通过向对象添加一个类似push的方法来“覆盖”push方法。这将有效地隐藏原型中的push方法实现。

 共享原型

JavaScript中原型的真正魔力在于多个对象如何维护对同一个原型对象的引用。例如,如果我们创建两个这样的数组:

var myArray=[1,2];var yourArray=[4,5,6];那么这两个数组将共享同一个原型对象,下面的代码计算结果为真:

object . getprototypeof(my array)==object . getprototypeof(your array);如果我们参考两个数组对象上的推送方法,JavaScript将在原型上寻找共享的推送方法。

图4

JavaScript中的原型对象提供了继承功能,同时实现了这种方法的共享。原型也是连锁的。换句话说,因为原型对象只是一个对象,所以一个原型对象可以维护对另一个原型对象的引用。如果您重新检查图2,您可以看到原型的__proto__属性是指向另一个原型的非空值。当JavaScript像push方法一样寻找成员时,它会检查原型引用链中的每个对象,直到找到该成员或到达原型链的末尾。原型链为继承和共享开辟了一条灵活的途径。

您可能会问的下一个问题是:如何为这些自定义对象设置原型引用?比如前面使用的点对象,如何将add方法添加到原型对象中,并从多个点对象继承该方法?在回答这个问题之前,我们需要先看看函数。

 有关函数

JavaScript中的函数也是对象。这种说法带来了几个重要的结果,但我们不会涵盖本文中的所有问题。其中,将函数赋给变量并将一个函数作为参数传递给另一个函数的能力构成了现代JavaScript编程表达式的基本范式。

我们需要注意的是,函数本身是一个对象,所以函数可以有自己的方法、属性和引用一个原型对象。让我们讨论以下代码的含义。

//这将返回true:type of(array)=' function '//此表达式也是:object . getprototypeof(array)==object . getprototypeof(function(){ })//此表达式相同:Array.prototype!=null代码中的第一行证明了JavaScript中的数组是一个函数。稍后我们将看到如何调用Array函数来创建一个新的数组对象。下一行代码证明了array对象与任何其他函数对象使用相同的原型,正如我们看到Array对象共享相同的原型一样。最后一行代码证明所有Array函数都有一个原型属性,这个原型属性指向一个有效的对象。这个原型属性非常重要。

JavaScript中的每个函数对象都有一个原型属性。切勿混淆此原型属性的__proto__属性。他们没有相同的目的,也没有指向相同的对象。

//返回trueobject.getprototypeof(数组)!=array . prototype array . _ _ proto _ _提供了一个数组原型-请将其视为数组函数继承的对象。

Array.protoype提供了所有数组的原型对象。也就是说,它提供了像myArray这样的数组对象的原型对象,并且还包含了所有数组都将继承的方法。我们可以写一些代码来证明这个事实。

//true array . prototype==object . get prototype of(my array)//也是true array . prototype==object . get prototype of(your array);我们也可以利用这些新知识重新绘制之前的原理图。

图5

根据您所知道的,想象一下创建一个新对象并使其行为像一个数组的过程。一种方法是使用下面的代码。

//创建新的空对象var o={ };//继承自同一个原型,一个数组对象o. _ _ proto _ _=Array.prototype//现在我们可以调用数组的任何方法.o . push(3);虽然这段代码很有趣,也很有效,但问题是,并不是每个JavaScript环境都支持可写__proto__ object属性。幸运的是,JavaScript确实有一个内置的创建对象的标准机制。它只需要一个操作符来创建新对象并设置新对象的__proto__引用——也就是“new”操作符。

var o=新数组();o . push(3);JavaScript中的新运算符有三个基本任务。首先,它创建一个新的空对象。接下来,它将设置新对象的__proto__属性,以匹配被调用函数的原型属性。最后,运算符调用函数,将新对象作为“this”引用传递。如果要扩展最后两行代码,它将如下所示:

var o={ };o . _ _ proto _ _ _=array . prototype;array . call(o);o . push(3);函数的调用方法允许您在调用函数时指定函数内部“this”引用的对象。当然,在这种情况下,函数的作者需要实现这样的函数。一旦作者创建了这样的函数,它就可以被称为构造函数。

构造器

构造函数与普通函数相同,但有以下两个特殊属性。

通常构造函数的第一个字母是大写的(这样更容易识别构造函数)。构造函数通常与新的操作符相结合来构造新的对象。数组是构造函数的一个例子。Array函数需要和新的运算符一起使用,Array的第一个字母是大写的。JavaScript包含Array作为内置函数,任何人都可以编写自己的构造函数。事实上,我们最终可以为之前创建的点对象编写一个构造函数。

var Point=函数(x,y){ this . x=x;this.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);在上面的代码中,我们使用了新的运算符和Point函数来构造一个具有x和y属性的点对象以及一个add方法。您可以想象最终的结果如图6所示。

图6

现在的问题是,我们仍然在每个点对象中有一个单独的add方法。使用我们所学的原型和继承知识,我们更喜欢将点对象的add方法从每个点实例转移到Point.prototype .要继承add方法,我们只需要修改Point.prototype对象。

var Point=函数(x,y){ this . x=x;this.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);你完蛋了!我们刚刚用JavaScript完成了原型继承模式!

图7

摘要

希望这篇文章能帮助你揭开JavaScript原型概念的神秘面纱。首先,我看到了原型如何使一个对象从其他对象继承函数,然后我看到了如何组合新的运算符和构造函数来构建一个对象。这里提到的只是解锁对象原型的力量和灵活性的第一步。本文鼓励您发现和学习关于原型和JavaScript语言的新信息。

同时,请小心驾驶。你永远不知道这些在路上行驶的车辆将从它们的原型中继承什么样的(有缺陷的)技术。

原创链接:剧本迷翻译:伯乐在线-爱洁

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