设为首页 加入收藏

TOP

JavaScript 原型与继承机制详解(一)
2017-07-13 10:22:55 】 浏览:324
Tags:JavaScript 原型 继承 机制 详解

引言


  初识 java script 对象的时候,我以为 JS 是没有继承这种说法的,虽说 JS 是一门面向对象语言,可是面向对象的一些特性在 JS 中并不存在(比如多态,不过严格来说也没有继承)。这就困惑了我很长的时间,当我学习到 JS 原型的时候,我才发现了 JS 的新世界。本篇文章讲解了 java script new 操作符与对象的关系、原型和对象关联(也就是俗称的继承)的原理,适合有一定基础的同学阅读。


  许多书籍上都会说到如何在 JS 当中定义“类”,通常来讲就是使用如下代码:


  实际上这一个很糟糕的语言机制,我们首先要明确,在 JS 当中根本没有“类”这种东西。在了解它之前,我们要先来了解下 JS 的发展历史。


  java script 随着互联网和浏览器而诞生,在早些年代,互联网还比较贫乏,上网的成本也比较高,网速非常的慢,通常需要花很长的时间才能传输完一个纯文本的 HTML 文件。所以那时候 Netscape 就提出,需要有一种解决方案,能使一些操作在客户端进行而不需要通过服务器处理,比如用户在填写邮箱的时候少写了一个“@”,在客户端就可以检查出错误并提示用户而不需要在服务器进行解析,这样就可以极大的降低通信操作带来了延迟和带宽消耗。而那时候,正巧 JAVA 问世,火的那叫个一塌糊涂,所以 Netscape 决定和 SUN 合作,在浏览器当中植入 JAVA 小程序(后来称Java applet)。不过后来就这一方案产生了争议,因为浏览器本来只需要很小的操作,而 JAVA 语言本身太“重”了,用来处理什么表单验证的问题实在是大材小用,所以决定开发一门新的语言来支持客户端的轻量级操作,而又要借鉴 JAVA 的语法。于是乎 Netscape 开发出了一门新的轻量级语言,在语法方面偏向于 C 和 JAVA,在数据结构方面偏向于 JAVA,这门语言最初叫做 Mocha,后来经过多年的演变,变成了现在的 java script。


  故事说道这里,好像和本文并没有什么关系...别急,马上就要说道点子上了。这个语言为什么要取名 java script 呢,其实它和 JAVA 并没有半毛钱的关系,只是因为在那点年代,面向对象方法问世才不久,所有的程序员都推崇学习面向对象方法,再加上 JAVA 的横空出世和大力宣传,只要和 JAVA 沾边的东西就像是往脸上贴了金一样,自带光环。所以便借助了 JAVA 的名气来进行宣传,不过光是嘴皮子宣传还不行,因为面向对象方法的推崇,大家都习惯于面向对象的语法,也就是 new Class() 的方法编写代码。不过 java script 语言本身并没有类的概念,其是多种语言的大杂烩,为了更加贴合习惯了面向对象语法的程序员,于是 new 操作符诞生了。


  好了,说了这么大一堆故事,就是想告诉同学们,new 操作符在 java script 当中本身就是一个充满歧义的东西,它并不存在类的概念,只是贴合程序员习惯而已。那么在 java script 当中 new 操作符和对象究竟有什么关系呢?思考下面这一段代码:


  咦?发生了什么奇怪的事情,x 和 y 哪里去了?实际上 new 操作符并不是传统面向对象语言那样,创建一个类的实例,new 操作符实际上只是在引擎内部帮我们在函数的开始创建好了一个对象,然后将函数的上下文绑定到这个对象上面,并在函数的末尾返回这个对象。这里需要注意的问题是,如果我们手动的返回了一个对象,那么按照函数执行机制,一旦返回了一个值,那么该函数也就执行结束,后面的代码将不会执行,所以说在刚才的例子中我们得到的对象只是我们手动定义的对象,并不是引擎帮我们创建的对象。 new 操作符实际上类似于以下操作:


  不过需要注意的是,new 操作符只接受 Object 类型的值,如果我们手动返回的是基本类型,则还是会返回 this :


  现在我们现在可以将 new 操作符定义成以下方法:


   java script 中存在类似继承的机制,但是又不是标准面向对象的继承,在 JS 中使用的是原型的机制。要记住,在 JS 中只有对象,没有类,对象的继承是由原型来实现,笼统的来说可以这样理解,一个对象是另一个对象的原型,那么便可以把它比作父类,子类既然也就继承了父类的属性和方法。


  [[prototype]] 是函数的一个属性,这个属性的值是一个对象,该对象是所有以该函数为构造器创造的对象的原型。可以把它近似的理解为父类对象,那么相应的,子类自然会继承父类的属性和方法。不过为什么要区分原型继承和类继承的概念呢?标准的面向对象方法,类是不具有实际内存空间,只是一个事物的抽象,对象才是事物的实体,而通过继承得到的属性和方法,同属于该对象,不同的对象各自都拥有独立的继承而来的属性。不过在 java script 当中,由于没有类的概念,一直都是对象,所以我们“继承”的,是一个具有实际内存空间的对象,也是实体,也就是说,所有新创建的子对象,他们共享一个父对象(后面我统称为原型),不会拥有独立的属性:


  还记得我们之前所说的 new 操作符的原理吗?new 操作符的本质不是实例化一个类,而是引擎贴合习惯了面向对象编程方法的程序员,所以说 [[prototype]] 属性本质上也是 new 操作符的一个副产物。这个属性只在函数上面有意义,该属性定义了 new 操作符产生的对象的原型。除了 [[prototype]] 可以访问到对象原型以外,还有一个非标准的方法,在每一个对象中都有一个 __proto__ 属性,这个属性直接关联到了该对象的原型。这种方法没有写入 W3C 的标准规范,但是却得到了浏览器的广泛支持,许多浏览器都提供了该方法以供访问对象的原型。(个人觉得 __proto__ 比 [[prototype]] 更能体现原型链的本质)


  除了使用 new 操作符和函数的 [[prototype]] 属性定义对象的原型之外,我们还可以直接在对象上显示的通过 __proto_ 来定义,这种定义对象原型的方式更能够体现出 java script 语言的本质,更能够使初学者理解原型链继承的机制。


  现在我们来完成之前那个自定义 new 操作(如果你还不能理解这个函数,没有关系,跳过它,这并不影响你接下来的学习):


  介绍完原型之后,同学们需要明确以下几个概念:


  那么 java script 当中的原型是如何实现相互关联的呢?JS 引擎又是如何查找这些关联的属性呢?如何实现多个对象的关联形成一条原型链呢?


  在上面这段代码,我们可以看出,对象的原型可以实现多层级的关联的操作,obj1 是 obj2 的原型, obj2 同时又是 obj3 的原型,这种多层级的原型关联,就是我们常说的原型链。在访问一个处于原型链当中的对象的属性,会沿着原型链对象一直向上查找,我们可以把这种原型遍历操作看成是一个单向的链表,每一个处于原型链的对象都是链表当中的一个节点,JS 引擎会沿着这条链表一层一层的向下查找属性,如果找到了一个与之匹配的属性名,则返回该属性的值,如果在原型链的末端(也就是 Object.prototype)都没有找到与之匹配的属性,则返回 undefined。要注意这种查找方式只会返回第一个与之匹配的属性,所以会发生属性屏蔽:


  若要访问原型的属性,则需要一层的一层的先向上访问原型对象:


  要注意的一点是,原型链的遍历只会发生在 [[getter]] 操作上,也就是取值操作,也可以称

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇JavaWeb项目实现文件上传动态显示.. 下一篇常见的排序算法总结(JavaScript)

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目