宝哥软件园

对javascript继承系统的深入分析

编辑:宝哥软件园 来源:互联网 时间:2021-09-03

最近,我参与了一个web项目,接触到了jquery和其他框架。虽然很好用,但我还是想学习Javascript。今天,我想分享一下我最近对js原型继承的理解。请改正我的缺点。

1.构造函数的原型属性和原型对象

刚接触js的时候,一般都是用函数new作为例子,不知道原因。只听说js中的函数是一个对象。原来js并没有采用Java等语言中的类继承系统,而是用原型对象来实现继承系统,具体就是用“构造函数”来实现类的功能。

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

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

原型属性原型指向的原型是一个对象实例。具体来说,如下图所示,如果构造函数Animal()有一个原型对象B,那么构造函数创建的所有实例都必须复制到B,也就是说,Animal()的实例a1的_proto_ attribute也指向原型对象B.因此,实例a1可以继承B的所有属性、方法和其他属性.

图1 js对象实例化实现

第二,空的物体

在javascript中,“空对象”是整个原型继承系统的基础,也是所有对象的基础。在引入“空对象”之前,必须引入“空对象”。

空对象null

Null不是“空对象”。作为javascript中的保留词,它的意思是:

(1)属于对象类型

(2)对象为空

作为一个对象类型,可以使用for…in来枚举它,但是作为一个null值,null没有方法和属性(包括构造函数,_proto_等)。),所以什么都不能枚举。如下例所示:

var num=0;for(var propertyName为null){ num;} Alert(num);//显示值为0

最重要的一点是,null没有原型,它不是从Object()构造函数(或其子类)实例化的,instanceof操作将返回false。

2.“空对象”

“空对象”是指由对象()构造的标准对象实例。例如:

obj=新对象();或者obj={ };空对象具有对象的所有特性,因此它可以访问预定义的属性和方法,如toString()和valueof。

3.“空对象”和空值之间的关系

如下图2红线所示,通过“Object.prototype._proto_”获取Object原型对象的-proto- attribute时,会得到“null”,因为null对象没有属性,表示“Object {}”

原型对象是原型链的末端。

图2 js类继承系统

第三,Javascript继承的实现和原型链的维护

(1)继承的实现

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

函数Animal(){ this . name=' Animal ';};函数Dog(){ };Dog.prototype=新动物();var d=new Dog();console . log(d . name);//“Animal”通过创建Animal类型的实例,并将其赋给构造函数Dog()的原型属性,即Animal是Dog的父类,从而实现类型继承。这样,Dog类型的实例d也可以访问Animal类型的名称属性。

(2)原型链

JS对象继承系统中有两个原型链:“内部原型链”和“构造器原型链”。如图3所示,黑色箭头表示路径是由构造函数的prototype属性维护的“构造函数原型链”。红色箭头表示路径是由对象实例的_proto_ property维护的“内部原型链”。

图3原型链

(3)原型链维护

图3显示构造器通过显示的原型构建原型链,对象实例也通过_ prototype _ attribute构建原型链。由于_ proto _是一个不可访问的内部属性(object _ proto _ attribute的值可以在Chrome中查看,但不能修改),因此无法从子类(狗)的实例Dog1访问整个原型链。因此,我们需要在图3中的“内部原型链”和“构造器原型链”之间找到一个连接点,这样当实例无法访问obj时。_proto_,构造函数可以访问内部原型链(串联两个原型链)。

要从子类的实例访问整个原型链,需要使用实例的构造函数属性来维护原型链。

实际上,JavaScript维护了构造函数的原型属性。根据下面的测试代码,当我们自定义一个构造函数时,它的原型对象是Object()类型的一个实例,但是默认情况下它的原型对象的构造函数属性总是指向构造函数本身,而不是它的父类对象。如图4中构造函数实例中蓝色框中的构造函数属性所示,该构造函数属性继承自原型对象,因此可以获得由自定义构造函数生成的实例,默认情况下,该自定义构造函数的构造函数属性总是指向该构造函数。

函数Animal(){ };var a=新动物();控制台日志(动物.原型);//Object(){ } console . log(Animal . prototype . constructor===Animal);//真//真

图4

因此,当_ prototype _ property不可访问时,实例a1的原型对象可以通过a1.constructor.prototype获得,但是,当我们自定义一个构造函数Dog()并手动将其原型属性值指定为Animal时,我们将Dog的父类指定为Animal。此时,access d1.constructor的值是Animal,不是Dog;从图5可以看出,dog和Dog的原型对象分别是由两个不同的构造器,Animal()和Dog()产生的。但是,它们的构造器属性指向同一个Animal,这与使用构造器属性串联两个原型链的假设相冲突。

图5

是构造器有问题还是原型有问题?从图5可以看出,原型继承所需的“复制行为”已经正确实现,原型对象属性可以从子类实例中访问。问题是,当原型对象被赋予子类构造函数Dog()时,原型对象的构造函数应该被“纠正”。ECMAScript 3标准提供的方法是保留prototype的构造函数属性,并在子类构造函数中初始化其实例对象的构造属性。代码如下:

函数Dog () {//初始化构造函数属性this.constructor=Dog//或者这个。构造函数=参数。被调用方;};Dog.prototype=新动物();//给出原型对象,实现继承

图6

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

摘要

以上是边肖介绍的javascript继承系统的相关知识,希望对大家有所帮助。如果你有任何问题,请给我留言,边肖会及时回复你。非常感谢您对我们网站的支持!

更多资讯
游戏推荐
更多+