浅谈javascript继承体系
最近做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)”。
- 空对象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继承体系的更多相关文章
- 浅谈javascript的原型及原型链
浅谈javascript的原型及原型链 这里,我们列出原型的几个概念,如下: prototype属性 [[prototype]] __proto__ prototype属性 只要创建了一个函数,就会为 ...
- 浅谈javascript函数节流
浅谈javascript函数节流 什么是函数节流? 函数节流简单的来说就是不想让该函数在很短的时间内连续被调用,比如我们最常见的是窗口缩放的时候,经常会执行一些其他的操作函数,比如发一个ajax请求等 ...
- 浅谈JavaScript中的闭包
浅谈JavaScript中的闭包 在JavaScript中,闭包是指这样一个函数:它有权访问另一个函数作用域中的变量. 创建一个闭包的常用的方式:在一个函数内部创建另一个函数. 比如: functio ...
- 浅谈JavaScript浮点数及其运算
原文:浅谈JavaScript浮点数及其运算 JavaScript 只有一种数字类型 Number,而且在Javascript中所有的数字都是以IEEE-754标准格式表示的.浮点数的精度问题 ...
- 浅谈 JavaScript 编程语言的编码规范
对于熟悉 C/C++ 或 Java 语言的工程师来说,JavaScript 显得灵活,简单易懂,对代码的格式的要求也相对松散.很容易学习,并运用到自己的代码中.也正因为这样,JavaScript 的编 ...
- 浅谈JavaScript中的null和undefined
浅谈JavaScript中的null和undefined null null是JavaScript中的关键字,表示一个特殊值,常用来描述"空值". 对null进行typeof类型运 ...
- 浅谈JavaScript中的正则表达式(适用初学者观看)
浅谈JavaScript中的正则表达式 1.什么是正则表达式(RegExp)? 官方定义: 正则表达式是一种特殊的字符串模式,用于匹配一组字符串,就好比用模具做产品,而正则就是这个模具,定义一种规则去 ...
- [转载]浅谈JavaScript函数重载
原文地址:浅谈JavaScript函数重载 作者:ChessZhang 上个星期四下午,接到了网易的视频面试(前端实习生第二轮技术面试).面了一个多小时,自我感觉面试得很糟糕的,因为问到的很多问题都 ...
- 浅谈JavaScript的面向对象和它的封装、继承、多态
写在前面 既然是浅谈,就不会从原理上深度分析,只是帮助我们更好地理解... 面向对象与面向过程 面向对象和面向过程是两种不同的编程思想,刚开始接触编程的时候,我们大都是从面向过程起步的,毕竟像我一样, ...
随机推荐
- 201521123059 《Java程序设计》第五周学习总结
1. 本周学习总结 1.1 尝试使用思维导图总结有关多态与接口的知识点. 1.2 可选:使用常规方法总结其他上课内容. 1.Comparable与Arrays.sort,其功能是对指定对象数组按升序进 ...
- 归纳一下input中span提示以及input中onchange事件
一.当input中不含有onclick事件的时候 定义一个class为tip1的span: <td><input type=text name='POSTNAME' nameVal ...
- java面试题整理(1)
1.Equals与==的区别? ==是判断两个变量或者实例是不是指向同一个内存地址 equals是判断两个变量或者实例所指向的内存地址中的值是不是相同 2.Object有哪些公用方法? 方法equal ...
- Spring在JSP页面使用ServletContext
在 JSP 页面使用Application 可以 看到使用的是WebApplicationContextUtils 而不是WebApplicationContext.ROOT_WEB_APPLICAT ...
- angularui 自定义选项卡
ng-include 选取ng-template <!DOCTYPE html> <html lang="en" ng-app="myApp" ...
- 云 MongoDB 优化让 LBS 服务性能提升十倍
欢迎大家前往腾讯云技术社区,获取更多腾讯海量技术实践干货哦~ 随着国内服务共享化的热潮普及,共享单车,共享雨伞,共享充电宝等各种服务如雨后春笋,随之而来的LBS服务定位问题成为了后端服务的一个挑战.M ...
- 转载 iOS拦截导航栏返回按钮事件的正确方式
原文链接:http://www.jianshu.com/p/25fd027916fa 当我们使用了系统的导航栏时,默认点击返回按钮是 pop 回上一个界面.但是在有时候,我们需要在点击导航栏的返回按钮 ...
- Oculus关于Internal Error:OVR53225466报错解决方法
安装Oculus过程中可能会出现Internal Error:OVR53225466报错提示,如附件所示: 解决方法:修改hosts文件 操作方法: (1)以管理员方式打开记事本: (2)打开C:\W ...
- Java 中与(&)短路与(&&)以及 或(|)短路或(||)的关系
一.逻辑运算符的使用 1)逻辑运算符的连接的是布尔表达式,要与位运算符做区分. 2)使用方法: public class Test { public static void main(String[ ...
- spring-mvc List及数组的配置接收
数组接收 前台传递数组id 后台接收方式: public WebReturnObject deleteBatch(@RequestParam("id[]") String[] id ...