课程咨询 :186 8716 1620      qq:2066486918

昆明Java培训 > 达内新闻 > JavaScript中的原型和继承
  • JavaScript中的原型和继承

    发布:昆明Java培训      来源:达内新闻      时间:2016-10-19

  • 最近昆明Java培训班的老师观看24 Hours of Le Mans,这是法国流行的一项赛事。最快的车被称为Le Mans原型车。这些车虽然是由“奥迪”或“标致”这些厂商制造的,可它们并不是你在街上或速公路上所见到的那类汽车。它们是专为参加高速耐力赛事而制造出来的。

    厂家投入巨额资金,用于研发、设计、制造这些原型车,而工程师们总是努力尝试将这项工程做到极致。他们在合金、生物燃料、制动技术、轮胎的化合物成分和安全特性上进行了各种实验。随着时间的推移,这些实验中的某 技术经过反复改进,随之进入到车辆的主流产品线中。你所驾驶车辆的某些技术,有可能是在赛车原型上第一次亮相的。

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

    到现在,我们就有讨论JavaScript中的原型和继承问题的基础了。它虽然并不像你在C++、Java或C#中了解的经典继承模式一样,但这种方式同样强大,并且有可能会更加灵活。

    有关对象和类

    JavaScript中全是对象,这指的是传统意义上的对象,也就是“一个包含了状态和行为的单一实体”。例如,JavaScript中的数组是含有数个值,并且包含push、reverse和pop方法的对象。

    var myArray = [1, 2];

    myArray.push(3);

    myArray.reverse();

    myArray.pop();

    var length = myArray.length;

    现在问题是,push这样的方法是从何而来的呢?我们前面提到的那些静态语言使用“类语法”来定义对象的结构,但是JavaScript是一个没有“类语法”的语言,无法用Array“类”的语法来定义每个数组对象。而因为JavaScript是动态语 言,我们可以在实际需要的情况下,将方法任意放置到对象上。例如下面的代码,就在二维空间中,定义了用来表示一个点的点对象,同时还定义了一个add方法。

    var point = {

    x : 10,

    y : 5,

    add: function(otherPoint) {

    this.x += otherPoint.x;

    this.y += otherPoint.y;

    }

    };

    但是上面的做法可扩展性并不好。我们需要确保每一个点对象都含有一个add方法,同时也希望所有点对象都共享同一个add方法的实现,而不是这个方法手工添加每一个点对象上。这就是原型发挥它作用的地方。

    有关原型

    在JavaScript中,每个对象都保持着一块隐藏的状态——一个对另一个对象的引用,也被称作原型。我们之前创建的数组引用了一个原型对象,我们自行创建的点对象也是如此。上面说原型引用是隐藏的,但也有ECMAScript(JavaScript 正式名称)的实现可以通过一个对象的__proto__属性(例如谷歌浏览器)访问到这个原型引用。从概念上讲,我们可以将对象当作类似于图1所表示的对象——原型的关系。

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

    Object.getPrototypeOf(point) != Object.getPrototypeOf(myArray);

    对于本文的其余部分,最近昆明Java培训班的老师观看24 Hours of Le Mans,这是法国流行的一项赛事。最快的车被称为Le Mans原型车。将交叉使用__proto__和Object.getPrototypeOf函数,主要是因为__proto__在图和句子中更容易识别。需要记住的是它(__proto__)不是标准,而Object.getPrototypeOf函数才是查看对象 型的推荐方法。

    是什么让原型如此特别?

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

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

    myArray.push(3);

    了解其工作原理的第一步,是要认识到原型并不是特别的。原型只是普通的对象。可以给原型添加方法,属性,并把他们当作其他JavaScript对象一样看待。然而,套用乔治·奥威尔的小说《动物农场》中“猪”的说法——所有的 象应当是平等的,但有些对象(遵守规则的)比其他人更加平等。

    JavaScript中的原型对象的确是特殊的,因为他们遵从以下规则。当我们告诉JavaScript我们要调用一个对象的push方法,或读取对象的x属性时,运行时会首先查找对象本身。如果运行时找不到想要的东西,它就会循着__proto__引用和对 原型寻找该成员。当我们 调用myArray的push方法时,JavaScript并没有在myArray对象上发现push方法,而是在myArray的原型对象上找到了,于是JavaScript调用此方法(见图3)。

    上面昆明Java培训班的老师所描述的行为是指一个对象本身继承了原型上的任何方法或属性。JavaScript中其实不需要使用类语法也能实现继承。就像从赛车原型上继承了相应的技术的车,一个JavaScript对象也可以从原型对象上继承 能特性。

    展示了每个数组对象同时也可以维护自身的状态和成员。在请求得到myArray的length属性的情况下,JavaScript会取得myArray中length属性的值,而不会去读取原型中的对应值。我们可以通过向对象上添加push这样的方法来“重写”push方法 。这样就会有效地隐藏原型中的push方法实现。

    共享原型

    JavaScript中原型的真正神奇之处是多个对象如何维持对同一个原型对象的引用。例如,如果我们创建了这样的两个数组:

    var myArray = [1, 2];

    var yourArray = [4, 5, 6];

    那么这两个数组将共享同一个原型对象,而下面的代码计算结果为true:

    Object.getPrototypeOf(myArray) === Object.getPrototypeOf(yourArray);

    如果我们引用两个数组对象上的push方法,JavaScript会去寻找原型上共享的push方法。

    JavaScript中的原型对象提供继承功能,同时也就实现了该方法实现的共享。原型也是链式的。换句话说,因为原型对象只是一个对象,所以一个原型对象可以维持到另一个原型对象的引用。如果你重新审视便可以看到,原型的__pr oto__属性是一个指向另一个原型的非空值。当JavaScript查找像push方法这样的成员时,它会循着原型引用链检查每一个对象,直到找到该成员,或者抵达原型链的末端。原型链为继承和共享开辟了一条灵活的途径。

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

    有关函数

    JavaScript中的函数也是对象。这样的表述带来了几个重要的结果,而我们并不会在本文中涉及所有的事项。这其中,能将一个函数赋值给一个变量,并且将一个函数作为参数传递给另一个函数的能力构成了现代JavaScript编程表达的 基本范式。

    我们需要关注的是,函数本身就是对象,因此函数可以有自身的方法,属性,并且引用一个原型对象。让我们来讨论下面的代码的含义。

    //这将返回true:

    typeof (Array) === "function"

    //这样的表达式也是:

    Object.getPrototypeOf(Array) === Object.getPrototypeOf(function () { })

    //这样的表达式同样:

    Array.prototype != null

    代码中的第一行证明,JavaScript中的数组是函数。稍后我们将看到如何调用Array函数创建一个新的数组对象。下一行代码,证明了Array对象使用与任何其他函数对象相同的原型,就像我们看到数组对象间共享相同的原型一样。最后 一行代码证明了Array函数都有一个prototype属性,而这个prototype属性指向一个有效的对象。这个prototype属性十分重要。

    JavaScript中的每一个函数对象都有prototype属性。千万不要混淆这个prototype属性的__proto__属性。他们用途并不相同,也不是指向同一个对象。

    //返回true

    Object.getPrototypeOf(Array) != Array.prototype

    Array.__proto__提供的是数组原型–请把它当作Array函数所继承的对象。

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

    // true

    Array.prototype == Object.getPrototypeOf(myArray)

    //也是true

    Array.prototype == Object.getPrototypeOf(yourArray);

    我们也可以使用这项新知识重绘之前的示意图。

    基于所知道的知识,请想象创建一个新的对象,并让新对象表现地像数组的过程。一种方法是使用下面的代码。

    //创建一个新的空对象

    var o = {};

    //继承自同一个原型,一个数组对象

    o.__proto__ = Array.prototype;

    //现在我们可以调用数组的任何方法...

    o.push(3);

    虽然这段代码很有趣,也能工作,可问题在于,并不是每一个JavaScript环境都支持可写的__proto__对象属性。幸运的是,JavaScript确实有一个创建对象内建的标准机制,只需要一个操作符,就可以创建新对象,并且设置新对象的__prot o__引用–那就是“new”操作符。

    var o = new Array();

    o.push(3);

    JavaScript中的new操作符有三个基本任务。首先,它创建新的空对象。接下来,它将设置新对象的__proto__属性,以匹配所调用函数的原型属性。最后,操作符调用函数,将新对象作为“this”引用传递。如果要扩展最后两行代码,就 会变成如下情况:

    var o = {};

    o.__proto__ = Array.prototype;

    Array.call(o);

    o.push(3);

    函数的call方法允许你在调用函数的情况下在函数内部指定“this”所引用的对象。当然,函数的作者在这种情况下需要实现这样的函数。一旦作者创建了这样的函数,就可以将其称之为构造函数。

    构造函数

    构造函数和普通的函数一样,但是具有以下两个特殊性质。

    通常构造函数的首字母是大写的(让识别构造函数变得更容易)。

    构造函数通常要和new操作符结合,用来构造新对象。

    Array就是一个构造函数的例子。Array函数需要和new操作符一起使用,而且Array的首字母是大写的。JavaScript将Array作为内置函数包括在内,而任何人都可以写出自己的构造函数。事实上,我们最后可以为先前创建的点对象编写出构 函数。

    var Point = function (x, y) {

    this.x = x;

    this.y = y;

    this.add = function (otherPoint) {

    this.x += otherPoint.x;

    this.y += otherPoint.y;

    }

    }

    var p1 = new Point(3, 4);

    var p2 = new Point(8, 6);

    p1.add(p2);

    在上面的代码中,我们使用了new操作符和Point函数来构造点对象,这个对象带有x属性和y属性和一个add方法。

    现在的问题是我们的每个点对象中仍然有单独的add方法。使用我们学到的原型和继承的知识,我们更希望将点对象的add方法从每个点实例中转移到Point.prototype中。要达到继承add方法的效果,我们所需要做的,就是修改Point.prototyp e对象。

    var Point = function (x, y) {

    this.x = x;

    this.y = y;

    }

    Point.prototype.add = function (otherPoint) {

    this.x += otherPoint.x;

    this.y += otherPoint.y;

    }

    var p1 = new Point(3, 4);

    var p2 = new Point(8, 6);

    p1.add(p2);

    大功告成!我们刚刚在JavaScript中完成原型式的继承模式!

    总结

    昆明Java培训班的老师希望这篇文章能够帮助你揭开JavaScript原型概念的神秘面纱。开始看到的是原型怎样让一个对象从其他对象中继承功能,然后看到怎样结合new操作符和构造函数来构建对象。这里所提到的,只是开启对象原型 力量和灵活性的第一步。本文昆明Java培训班的老师鼓励你自己发现学习有关原型和JavaScript语言的新信息。

    推荐文章

上一篇:分享Elasticsearch---基础篇

下一篇:Java-链表LinkedList源码原理分析

最新开班日期  |  更多

Java--零基础全日制班

Java--零基础全日制班

开班日期:11/30

Java--零基础业余班

Java--零基础业余班

开班日期:11/30

Java--周末提升班

Java--周末提升班

开班日期:11/30

Java--零基础周末班

Java--零基础周末班

开班日期:11/30

  • 网址:http://km .java.tedu.cn      地址:昆明市官渡区春城路62号证券大厦附楼6楼
  • 课程培训电话:186 8716 1620      qq:2066486918    全国服务监督电话:400-827-0010
  • 服务邮箱 ts@tedu.cn
  • 2001-2016 达内国际公司(TARENA INTERNATIONAL,INC.) 版权所有 京ICP证08000853号-56