深入浅析javascript继承体系


Posted in Javascript onOctober 23, 2017

最近做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的所有属性、方法和其他性质。

深入浅析javascript继承体系

图1 js对象实例化实现

二、空的对象

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

空对象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 {}”

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

深入浅析javascript继承体系

图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_属性保持的“内部原型链”。

深入浅析javascript继承体系

图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

深入浅析javascript继承体系

图4

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

深入浅析javascript继承体系

图5

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

function Dog () {
//初始化constructor属性

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

};

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

深入浅析javascript继承体系

图6

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

总结

以上所述是小编给大家介绍的javascript继承体系的相关知识,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
如何用javascript判断录入的日期是否合法
Jan 08 Javascript
js用正则表达式来验证表单(比较齐全的资源)
Nov 17 Javascript
javascript实现简单的Map示例介绍
Dec 23 Javascript
Document.location.href和.replace的区别示例介绍
Mar 04 Javascript
快速掌握Node.js环境的安装与运行方法
Feb 16 Javascript
javascript中闭包概念与用法深入理解
Dec 15 Javascript
jQuery插件HighCharts绘制的基本折线图效果示例【附demo源码下载】
Mar 07 Javascript
使用node打造自己的命令行工具方法教程
Mar 26 Javascript
JS文件中加载jquery.js的实例代码
May 05 jQuery
微信小程序实现倒计时补零功能
Jul 09 Javascript
微信小程序学习总结(三)条件、模板、文件引用实例分析
Jun 04 Javascript
JavaScript实现简单图片切换
Apr 29 Javascript
Vue.js组件通信的几种姿势
Oct 23 #Javascript
Vue2.0+ElementUI实现表格翻页的实例
Oct 23 #Javascript
JavaScript之创意时钟项目(实例讲解)
Oct 23 #Javascript
浅谈js的解析顺序 作用域 严格模式
Oct 23 #Javascript
基于Vue2.0+ElementUI实现表格翻页功能
Oct 23 #Javascript
React操作真实DOM实现动态吸底部的示例
Oct 23 #Javascript
利用pm2部署多个node.js项目的配置教程
Oct 22 #Javascript
You might like
PHP Ajax中文乱码问题解决方法
2009/02/27 PHP
php获取当前网址url并替换参数或网址的方法
2010/06/06 PHP
php实现Session存储到Redis
2015/11/11 PHP
PHP使用redis实现统计缓存mysql压力的方法
2015/11/14 PHP
php使用glob函数遍历文件和目录详解
2016/09/23 PHP
Laravel 框架返回状态拦截代码
2019/10/18 PHP
Laravel自动生成UUID,从建表到使用详解
2019/10/24 PHP
js保存当前路径(cookies记录)
2010/12/14 Javascript
jquery 回车事件实现代码
2011/08/23 Javascript
防止xss和sql注入:JS特殊字符过滤正则
2013/04/18 Javascript
Extjs4中的分页应用结合前后台
2013/12/13 Javascript
jQuery对象和DOM对象之间相互转换的方法介绍
2015/02/28 Javascript
javascript中加var和不加var的区别 你真的懂吗
2016/01/06 Javascript
jQuery基于$.ajax设置移动端click超时处理方法
2016/05/14 Javascript
JavaScript判断是否是微信浏览器
2016/06/13 Javascript
微信小程序视图template模板引用的实例详解
2017/09/20 Javascript
JS基于递归实现网页版计算器的方法分析
2017/12/20 Javascript
Vue2.0 v-for filter列表过滤功能的实现
2018/09/07 Javascript
vue项目中实现的微信分享功能示例
2019/01/21 Javascript
微信公众号平台接口开发 获取access_token过程解析
2019/08/14 Javascript
[01:14]3.19DOTA2发布会 三代刀塔人第二代
2014/03/25 DOTA
[00:48]完美“圣”典2016风云人物:xiao8宣传片
2016/11/30 DOTA
[53:23]Secret vs Liquid 2018国际邀请赛淘汰赛BO3 第二场 8.25
2018/08/29 DOTA
[41:13]完美世界DOTA2联赛PWL S2 Forest vs Rebirth 第一场 11.20
2020/11/20 DOTA
[01:00:17]DOTA2-DPC中国联赛 正赛 SAG vs Dynasty BO3 第二场 1月25日
2021/03/11 DOTA
Python中将两个或多个list合成一个list的方法小结
2019/05/12 Python
利用Python进行图像的加法,图像混合(附代码)
2019/07/14 Python
使用Python+Appuim 清理微信的方法
2021/01/26 Python
德国孕妇装和婴童服装网上商店:bellybutton
2018/04/12 全球购物
Bed Bath & Beyond加拿大官网:购买床上用品、浴巾、厨房电器等
2019/10/04 全球购物
有模特经验的简历自我评价
2013/09/19 职场文书
应用电子技术专业个人求职信
2013/09/21 职场文书
运动会拉拉队口号
2014/06/09 职场文书
会议接待欢迎标语
2014/10/08 职场文书
工作表扬信
2015/01/17 职场文书
php去除数组中为0的元素的实例分析
2021/11/17 PHP