来自 服务器&运维 2019-12-04 16:27 的文章
当前位置: 澳门威尼斯人平台 > 服务器&运维 > 正文

JavaScript的六种继承方式_javascript技巧_脚本之家

澳门威尼斯人平台,javascript虽然是一门面向对象的语言,但是它的继承机制从一开始设计的时候就不同于传统的其他面向对象语言,是基于原型的继承机制,但是在这种机制下,继承依然有一些不同的实现方式。

复制代码 代码如下: function parent(){ this.x=10; } function child(){ var parentObj=new parent(); forthis[p]=parentObj[p]; } var childObj=new child; 复制代码 代码如下: function parent(){ this.x=10; } function child(){ this.parent=parent; this.parent(); delete this.parent; } var childObj=new child; 复制代码 代码如下: function parent(){ this.x=10; } function child; } var childObj=new child; 原型抄写 复制代码 代码如下: function parent(){ } parent.prototype.x=1; function child(){ } for(var p in parent.prototype)child.prototype[p]=parent.prototype[p]; child.prototype.y=2; var childObj=new child; 复制代码 代码如下: function parent{ var child=new Function; return child; } var child=new parent; var childObj=new child; 复制代码 代码如下: function parent(){ this.x=10; } function child(){ } child.prototype=new parent(); var childObj=new child; 复制代码 代码如下: function parent(){ this.x=10; } function child(){ var ret=new parent(); ret.y=20; return ret; } var childObj=new child;

继承是面向对象编程中又一非常重要的概念,JavaScript支持实现继承,不支持接口继承,实现继承主要依靠原型链来实现的。

ECMAScript只支持实现继承,而且其实现继承主要是依靠原型链来实现的。

方法一:类式继承

原型链

原型链

所谓的类式继承就是指模仿传统面向对象语言的继承方式,继承与被继承的双方都是“类”,代码如下:

首先得要明白什么是原型链,在一篇文章看懂proto和prototype的关系及区别中讲得非常详细

原型链的基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。每一个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的指针。如果:我们让原型对象A等于另一个类型B的实例,那么原型对象A就会有一个指针指向B的原型对象,相应的B的原型对象中保存着指向其构造函数的指针。假如B的原型对象又是另一个类型的实例,那么上述的关系依旧成立,如此层层递进,就构成了实例与原型的链条。

function Person{ this.name=name; } Person.prototype.getName=function(){ return this.name; };

原型链继承基本思想就是让一个原型对象指向另一个类型的实例

实例以及构造函数和原型之间的关系图如下所示:

该父类person的属性在构造函数中定义,可以保证继承它的子类的name属性不与它共享这个属性,而是单独属于子类,将getName方法挂载到原型上,是为了让继承它的子类的多个实例共享这一个方法体,这样能够节省内存,(对于多个实例来讲,每次New一个实例出来都会保证这些实例的getName方法引用的是同一段内存空间,而不是独立的空间)。

function SuperType() { this.property = true } SuperType.prototype.getSuperValue = function () { return this.property } function SubType() { this.subproperty = false } SubType.prototype = new SuperType() SubType.prototype.getSubValue = function () { return this.subproperty } var instance = new SubType() console.log(instance.getSuperValue // true 

person.constructor现在指向的是Parent,这是因为Child.prototype指向了Parent的原型,而Parent原型对象的constructor指向Parent。

定义一个继承方法extend,如下:

代码定义了两个类型SuperType和SubType,每个类型分别有一个属性和一个方法,SubType继承了SuperType,而继承是通过创建SuperType的实例,并将该实例赋给SubType.prototype实现的。

当以读取模式访问一个实例属性时,首先会在实例中搜索该属性,如果没有找到该属性,则会继续搜索实例的原型。在通过原型链实现的集成中,搜索过程就会沿着原型链继续向上,直到搜索到原型链的末端。

function extend{ var F=function(){}; F.prototype=superClass.prototype; subClass.prototype=new F(); subClass.prototype.constructor=subClass; subClass.superClass=superClass.prototype; if(superClass.prototype.constructor==Object.prototype.constructor){ superClass.prototype.constructor=superClass; } }

实现的本质是重写原型对象,代之以一个新类型的实例,那么存在SuperType的实例中的所有属性和方法,现在也存在于SubType.prototype中了。

例如,调用person.getParentValue搜索Child.prototype;3)搜索Parent.prototype;找到了getParentValue()方法停止。

在这个方法中,首先创建一个新的类为F,让它的原型为父类的原型,并且让子类的原型指向该类F的一个实例,从而达到了一个继承父类的目的,同时,子类的原型由于被修改,所以将修改后的原型的constructor属性指向子类,让其拥有构造函数,同时给该子类挂载一个superClass属性,子类可以通过该属性调用父类,从而建立起了子类和父类的关系。

我们知道,在创建一个实例的时候,实例对象中会有一个内部指针指向创建它的原型,进行关联起来,在这里代码SubType.prototype = new SuperType(),也会在SubType.prototype创建一个内部指针,将SubType.prototype与SuperType关联起来。

1、默认的原型

定义一个子类Author来继承父类Person,如下:

所以instance指向SubType的原型,SubType的原型又指向SuperType的原型,继而在instance在调用getSuperValue()方法的时候,会顺着这条链一直往上找。

前面的例子中展示的原型链少了一环,所有引用类型默认都继承了Object,而这个继承也是通过原型链实现的。因此默认的原型都包含一个内部指针,指向Object.prototype,这也正是所有自定义类型会继承toString等默认方法的根本原因。换句话说Object.prototype就是原型链的末端。

function Author{ Author.superClass.constructor.call; this.book=books; } extend; Author.prototype.getBooks=function(){ return this.book; }

添加方法

2、确定原型和实例的关系

这里在子类的构造函数中通过其superClass属性调用父类的构造函数,同时采用call方法,转换该方法调用的this指向,让子类Author也拥有父类的属性,同时子类又拥有自己的属性book,所以在构造函数中将参数books赋值给属性book,达到构造的目的。采用extend函数继承父类Person原型上的属性和方法(实际上只继承了方法,因为我们之前只是将方法挂载到了原型上,属性是在构造函数中定义的)。同时,Author又拥有自己的方法getBooks,将其挂载到对应的原型上,达到了在继承的基础上进一步扩充自身的目的。

在给SubType原型添加方法的时候,如果,父类上也有同样的名字,SubType将会覆盖这个方法,达到重新的目的。 但是这个方法依然存在于父类中。

通过两种方式可以确定原型和实例之间的关系,第一种是使用instanceOf操作符,第二种是使用isPrototypeOf()方法。 实例 instanceOf 原型链 中出现过的构造函数,都会返回true

这种继承方式很明显就是采用类似于传统面向对象语言的类式继承,优点是对习惯于传统面向对象概念的程序员来讲很容易理解,缺点是过程比较繁琐,内存消耗稍大,因为子类也拥有自己的构造函数及原型,而且子类和父类的属性完全是隔离的,即使两者是一样的值,但是不能共享同一段内存。

记住不能以字面量的形式添加,因为,上面说过通过实例继承本质上就是重写,再使用字面量形式,又是一次重写了,但这次重写没有跟父类有任何关联,所以就会导致原型链截断。

console.log(person instanceOf Child);//true console.log(person instanceOf Parent);//true console.log(person instanceOf Object);//true isPrototype(),只要是原型链中出现过的原型,都可以说是该原型链所派生出来的实例的原型,因此也返回true. console.log(Object.prototype.isPrototypeOf;//true console.log(Parent.prototype.isPrototypeOf;//true console.log(Child.prototype.isPrototypeOf;//true 

方法二:原型式继承

function SuperType() { this.property = true } SuperType.prototype.getSuperValue = function () { return this.property } function SubType() { this.subproperty = false } SubType.prototype = new SuperType() SubType.prototype = { getSubValue:function () { return this.subproperty } } var instance = new SubType() console.log(instance.getSuperValue // error 

3、谨慎地定义方法

首先定义一个父类,这里不会刻意模仿使用构造函数来定义,而是直接采用对象字面量的方式定义一个对象,该对象就是父类

问题

子类型有时候需要覆盖超类型中的某个方法,或者需要添加超类型中不存在的莫个方法,注意:给原型添加方法的代码一定要放在替换原型的语句之后。

var Person={ name:'default name', getName:function(){ return this.name; } } ;

单纯的使用原型链继承,主要问题来自包含引用类型值的原型。

当通过Child的实例调用getParentValue()时,调用的是这个重新定义过的方法,但是通过Parent的实例调用getParentValue()时,调用的还是原来的方法。

和第一种方法一样,该对象拥有一个属性name和一个方法getName .

function SuperType() { this.colors = ['red', 'blue', 'green'] } function SubType() { } SubType.prototype = new SuperType() var instance1 = new SubType() var instance2 = new SubType() instance1.colors.push console.log // ["red", "blue", "green", "black"] console.log // ["red", "blue", "green", "black"] 

格外需要注意的是:必须要在Parent的实例替换原型之后,再定义这两个方法。

然后再定义一个克隆方法,用来实现子类对父类的继承,如下:

在SuperType构造函数定义了一个colors属性,当SubType通过原型链继承后,这个属性就会出现SubType.prototype中,就跟专门创建了SubType.prototype.colors一样,所以会导致SubType的所有实例都会共享这个属性,所以instance1修改colors这个引用类型值,也会反映到instance2中。

还有一点需要特别注意的是:通过原型链实现继承时,不能使用对象字面量创建原型方法,因为这样做会重写原型链。

function clone{} F.prototype=obj; return new F(); }

借用构造函数

以上代码刚把Parent的实例赋值给Child的原型对象,紧接着又将原型替换成一个字面量,替换成字面量之后,Child原型实际上包含的是一个Object的实例,而不再是Parent的实例,因此我们设想中的原型链被切断.Parent和Child之间没有任何关联。

该克隆方法新建一个对象,将该对象的原型指向父类即参数obj,同时返回这个对象。

此方法为了解决原型中包含引用类型值所带来的问题。

4、原型链的问题

本文由澳门威尼斯人平台发布于服务器&运维,转载请注明出处:JavaScript的六种继承方式_javascript技巧_脚本之家

关键词: