最近做web项目,接触了jquery等框架,虽然使用方便,但是还是想学习下Javascript,今天分享下最近对js原型继承的理解,不足之处欢迎指正。

一、构造器的原型属性与原型对象

  刚接触js时通常依样画瓢,用函数new一个实例,也不知道其原因,只听说js中函数即对象。原来js中没有采用Java等语言中的类继承体系,而是使用原型对象(prototype)实现继承体系,具体说是利用“构造器”实现类的功能。

首先解释下原型继承中的两个重要概念:原型属性、原型对象(实例)。

就js对象系统而言,创建的每个函数(构造器)都有一个prototype原型属性,同时,通过构造器创建的每个对象实例也包含一个_proto_属性,prototype和_proto_属性是一个指针,指向原型对象。普通函数与构造函数的唯一区别就是,其原型属性prototype是不是一个有意义的值。

原型属性prototype所指向的原型是一个对象实例(Object instance)。具体如下图所示,若构造器Animal()有一个原型对象B,则由该构造器创建的实例都必然复制于B。即:Animal()的实例a1的_proto_属性也会指向原型对象B。因此,实例a1能够继承B的所有属性、方法和其他性质。

图1 js对象实例化实现

二、空的对象

在javascript中,“空的对象”是整个原型继承体系的根基,是所有对象的基础。介绍“空的对象”之前,必须先介绍下“空对象(null)”。

  1. 空对象null

  null不是“空的对象”,作为javascript中的一个保留字,其含义是:

  (1)属于对象类型

  (2)对象是空值

作为一个对象类型,可以使用for…in去列举它,但是作为一个空值,null没有任何方法和属性(包括constructor、_proto_等属性),因此列举不到任何内容。如下例所示:

  var num=0;

  for(var propertyName in null)

  {

  num++;

  }

  Alert(num);//显示值为0

最重要的一点是null没有原型,它并不是自Object()构造器(或其子类)实例化而来,对其进行instanceof 运算会返回false。

  2.“空的对象”

  “空的对象”是指一个标准的、通过Object()构造的对象实例。例如:

obj=new Object();或 obj={};

  “空的对象”具有“对象”的一切特性,因此可以存取toString()、valueof等预定义的属性和方法。

  3.“空的对象”与null的关系

   如下图2中红线所示路径,当通过”Object.prototype._proto_”获取Object原型对象的-proto-属性时,将会得到”null”,由于null对象没有任何属性,也就是说”Object {}”

  原型对象就是原型链的终点了。

图2 js类继承体系

三、Javascript继承的实现以及原型链维护

(1)继承的实现

  第一节说过javascript中类继承是通过修改构造函数的原型属性prototype实现的。如下代码所示:

function Animal() {

this.name = 'Animal';

};

function Dog() {

};

  Dog.prototype = new Animal();

var d = new Dog();

console.log(d.name);//'Animal'

  通过创建一个Animal类型的实例并将其赋值给构造函数Dog()的prototype属性,从而实现类型继承,即Animal是Dog的父类。这样Dog类型的实例d也能访问Animal类型的name属性。

(2)原型链

JS对象继承体系中有两种原型链:“内部原型链”和“构造器原型链”。如图3所示,黑色箭头指示路径是通过构造函数的prototype属性保持的“构造器原型链”。红色箭头指示路径是通过对象实例的_proto_属性保持的“内部原型链”。

图3 原型链

(3)原型链维护

图3说明构造器通过显示的prototype构建了一个原型链,而对象实例也通过_ proto _属性构建了一个原型链。由于_ proto _是一个不可访问的内部属性(Chrome中可以查看对象_ proto _属性的值,但不可以修改),因此无法从子类(Dog)的实例dog1开始访问整个原型链。因此,我们需要从图3中的“内部原型链”和“构造器原型链”中找到一个连接点,使得实例不能访问obj._proto_的情况下通过构造器访问内部原型链(将两种原型链串联起来)。

若要从子类的实例开始访问整个原型链,需要使用实例的constructor属性维护原型链。

其实,JavaScript已经为构造器维护了原型属性,根据如下测试代码,当我们自定义一个构造器时,其原型对象是一个Object()类型的实例,但是其原型对象的constructor属性默认总是指向构造器自身,而非指向其父类Object。如图4中构造器实例中蓝色框中的constructor属性,该constructor属性继承自原型对象,因此可以得出一个自定义的构造器产生的实例,其constructor属性默认总是指向该构造器。

  function Animal() {

};

var a = new Animal();

console.log(Animal.prototype);//Object(){}

console.log(Animal.prototype.constructor === Animal);//true//true

图4

  因此,在_proto_属性不可访问时,可通过a1.constructor.prototype获取实例a1的原型对象。然而,当我们自定义一个构造函数Dog(),并且手动指定其prototype属性值为Animal,即指定Dog的父类为Animal。此时访问d1.constructor值为Animal,而不是Dog;由图5可以看出,Dog的原型对象和dog分别由Animal()和Dog()两个不同的构造器产生,然而他们的constructor属性指向了相同的构造器(Animal),这样就与使用constructor属性串联两种原型链的设想冲突了。

图5

  是构造器出问题还是原型出了问题?图5可以看出,原型继承要求的“复制行为”已经正确实现,能够从子类实例中访问原型对象属性,问题是在给子类构造器Dog()赋予一个原型对象时应该“修正”该原型对象的构造属性值(constructor)。ECMAScript 3标准提供的方法是:保持原型的构造器属性,在子类构造器中初始化其实例对象的构造属性。代码如下:

  function Dog () {

  //初始化constructor属性

   this.constructor=Dog; //或 this.constructor=arguments.callee;

  };

  Dog.prototype = new Animal();//赋予原型对象,实现继承

图6

对constructor属性“修正”后效果如图6所示,在子类构造器Dog中初始化其实例对象的constructor属性后,Dog的实例对象的constructor都指向Dog,而Dog的原型对象的constructor仍然指向父类型构造器Animal。这样就可以实现利用constructor属性串联起原型链,可以从子类实例开始回溯整个原型链。

浅谈javascript继承体系的更多相关文章

  1. 浅谈javascript的原型及原型链

    浅谈javascript的原型及原型链 这里,我们列出原型的几个概念,如下: prototype属性 [[prototype]] __proto__ prototype属性 只要创建了一个函数,就会为 ...

  2. 浅谈javascript函数节流

    浅谈javascript函数节流 什么是函数节流? 函数节流简单的来说就是不想让该函数在很短的时间内连续被调用,比如我们最常见的是窗口缩放的时候,经常会执行一些其他的操作函数,比如发一个ajax请求等 ...

  3. 浅谈JavaScript中的闭包

    浅谈JavaScript中的闭包 在JavaScript中,闭包是指这样一个函数:它有权访问另一个函数作用域中的变量. 创建一个闭包的常用的方式:在一个函数内部创建另一个函数. 比如: functio ...

  4. 浅谈JavaScript浮点数及其运算

    原文:浅谈JavaScript浮点数及其运算     JavaScript 只有一种数字类型 Number,而且在Javascript中所有的数字都是以IEEE-754标准格式表示的.浮点数的精度问题 ...

  5. 浅谈 JavaScript 编程语言的编码规范

    对于熟悉 C/C++ 或 Java 语言的工程师来说,JavaScript 显得灵活,简单易懂,对代码的格式的要求也相对松散.很容易学习,并运用到自己的代码中.也正因为这样,JavaScript 的编 ...

  6. 浅谈JavaScript中的null和undefined

    浅谈JavaScript中的null和undefined null null是JavaScript中的关键字,表示一个特殊值,常用来描述"空值". 对null进行typeof类型运 ...

  7. 浅谈JavaScript中的正则表达式(适用初学者观看)

    浅谈JavaScript中的正则表达式 1.什么是正则表达式(RegExp)? 官方定义: 正则表达式是一种特殊的字符串模式,用于匹配一组字符串,就好比用模具做产品,而正则就是这个模具,定义一种规则去 ...

  8. [转载]浅谈JavaScript函数重载

     原文地址:浅谈JavaScript函数重载 作者:ChessZhang 上个星期四下午,接到了网易的视频面试(前端实习生第二轮技术面试).面了一个多小时,自我感觉面试得很糟糕的,因为问到的很多问题都 ...

  9. 浅谈JavaScript的面向对象和它的封装、继承、多态

    写在前面 既然是浅谈,就不会从原理上深度分析,只是帮助我们更好地理解... 面向对象与面向过程 面向对象和面向过程是两种不同的编程思想,刚开始接触编程的时候,我们大都是从面向过程起步的,毕竟像我一样, ...

随机推荐

  1. 201521123017 《Java程序设计》第1周学习总结

    1. 本章学习总结 (1)对JAVA的历史发展的了解 (2)JAVA运行环境的搭建和JVM,JDK,JRE的相关的JAVA开发工具的认识及其掌握 (3)写法的不同,开头public class 文件名 ...

  2. java课程设计——算术运算测试个人博客

    1.团队课程设计链接 团队博客:http://www.cnblogs.com/yytx/p/7064790.html 2.个人负责模块 有关排行榜的界面设计和代码编写 3.自己的代码提交记录截图 4. ...

  3. 让SAE下的wordpress支持文件上传

    非PHP程序员照着源码打的小布丁… SAE是不允许本地磁盘读写的,所以使用wordpress撰写文章的时候, 上传控件默认是用不了的,幸好SAE提供了storage服务来存储文件,那就可以修改word ...

  4. JavaSE集合(十)之Map

    前面给大家介绍了集合家族中的Collection家族,这一篇给大家分享的是集合中的另一个家族就是Map家族.以前的时候学习Map的时候没有很认真的去学习,我觉得很多东西还是不是很清楚. 这次我将总结的 ...

  5. python 浅析对return的理解

    最近很忙,但是还是很认真的学习python这个东西,不是出于什么目的,只是单纯的喜欢罢了.最近学习的东西比较简单,但是也遇到了一些问题,就是比较迷惑人的问题,今天小编就在这里讲讲自己的对return的 ...

  6. CacheConcurrencyStrategy五种缓存方式

    CacheConcurrencyStrategy有五种缓存方式:  CacheConcurrencyStrategy.NONE,不适用,默认  CacheConcurrencyStrategy.REA ...

  7. Apache POI

    Apache POI 用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程式对Microsoft Office格式档案读和写的功能.POI为"Po ...

  8. linux 命令随笔 ls cd pwd mkdir rm mv cp cat nl

    Linux 命令练习 ls命令 ls就是list的简写,目的是打印当前目录下的清单 格式 ls[选项][目录名] 常用参数 -a –all 列出目录下的所有文件,包括以 . 开头的隐含文件 -l 除了 ...

  9. 内核对象 windows操作系统

    问题: 什么是内核对象? 答:内核对象实际上时由内核分配的一块内存,而且只能由内核来访问.它时一个数据结构,成员包含有关于该对象的信息.一些成员对于所有对象类型都是一样的,比如对象名称.安全描述.使用 ...

  10. SSM框架—详细整合教程(Spring+SpringMVC+MyBatis)

    很久没有新搭建过框架了,今天搭建一遍.以往都是在eclipse中搭建,今天换Idea吧,目前来说Idea用的还是很多的,但是用习惯了eclipse的朋友,可能会不太习惯 ok.....开始: 注意区分 ...