`
ouyangfei0426
  • 浏览: 126961 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

JavaScript面向对象的继承机制——类式继承

 
阅读更多

JavaScript面向对象的继承机制——类式继承

前言

         昨天参加了公司的《js面向对象的组件》的培训,回来后有所感悟,结合自己以前学到的以及感悟的,今天在这里写出来,供大家分享。

         javascript中继承是一个复杂的话题,相对于其他面向对象的语言中的继承要复杂的多,在其他面向对象的语言中,继承一个类只需要使用一个关键字即可,比如java,只需要extended关键字即可实现继承,但在javascript中要达到继承的目的,需要进行一些额外的处理。JavaScript是属于使用原型式继承的少数语言之一,得益于JavaScript语言的灵活性,我们既可以使用标准的基于类继承,也可以使用原型式继承。由于时间有限,今天在这里先说说类式继承!原型继承等下次有时间再写。

类式继承

         Javascript可以装扮撑使用类式继承的语言。如下例是一个Person类:

function Person(name){

    this.name=name;

}

 

Person.prototype.getName=function(){

    return this.name;

};

要创建Person类的一个实例,只需编写如下代码即可:

var mark=new Person("mark");

mark.getName();

 

但是要创建一个继承Person的类则要复杂一些:

function Man(name,wife){

    Person.call(this,name);

    this.wife=wife;

}

 

Man.prototype=new Person();

Man.prototype.constructor=Man;

Man.prototype.getWife=function(){

    return this.wife;

};

代码中使用Man.prototype=new Person();Man.prototype.constructor=Man;

Person.call(this,name)语句实现Man继承Person类,其中Man.prototype=new Person();实现Man继承Person类的一个实例对象Man.prototype.constructor=Man;用于确保Man的构造函数被正确设置,Person.call(this,name)用于确保调用Person的公有成员时的指定其执行环境为Man实例对象,而不是Person的实例对象。

         让一个类继承另一个类需要很多行代码,而不像在java语言中只需要用一个extend即可。首先要做的就是像前面示例中那样创建一个构造函数,并在构造函数中调用超类的构造函数,并将参数传递给超类的构造函数。

然后是设置原型链。尽管相关代码比较简单,但是实际上这还是一个比较复杂的话题。在JavaScript中每个函数都有一个名为prototype的属性,这个属性要么指向另一个对象,要么为null。在访问对象的某个成员时,如果在当前对象中找不到该成员,那么JavaScript会沿着当前对象的prototype属性,去该属性指向的对象中查找该成员,找到就返回该成员,如果还是找不到,则继续沿着该属性指向的对象的prototype属性去查找该成员,直到找到或者已经查到原型链的最顶端的Object.prototype对象。这意味这让一个类继承另一个类,只需将子类的prototype设置为指向超类的一个实例对象即可。这与其他语言中的继承机制迥然不同。

为了让Man继承Person,必须手工将Manprototype设置为Person类的一个实例,最后一步是将prototypeconstructor属性重新设置为Man,因为把prototype属性设置为Person实例时,其constructor属性被抹除了(定义一个构造函数时,其默认的prototype属性指向的是一个空的对象,而且其constructor属性会被自动的设置为该构造函数本身,如果手工将其prototype属性设置为另一个对象,那么新对象自然不会具有原对象的constructor值,需要重新纠正其值)。

创建Man的实例对象的代码如下:

mark=new Man("mark","XXXX");

mark.getName();

mark.getWife();

 

由此可见类式继承的所有复杂性只限于类的声明,创建新实例的过程仍然很简单。为了简化类的声明,可以把派生子类的整个过程包装在一个extended函数中,代码如下:

function extend(subClass, superClass){

    var F = function(){};

    F.prototype = superClass.prototype;

    subClass.prototype = new F();

    subClass.prototype.constructor = subClass;//确保子类的构造函数被正确设置。

}

这个函数与我们之前手工做的几乎一样,它设置了prototype,然后重新设置了constructor值,作为一项改进,它添加了一个空函数F,并用它创建了一个实例对象插入原型链中,这样做可避免创建超类的新实例,因为超类的构造函数可能会进行一些额外的大量的计算任务。

使用extended函数后,前面的例子变为:

/**

 * Person

 * @param {Object} name

 */

function Person(name){

    this.name = name;

}

 

Person.prototype.getName = function(){

    return this.name;

};

 

 

/**

 * Man

 * @param {Object} name

 * @param {Object} wife

 */

function Man(name, wife){

    Person.call(this, name);

    this.wife = wife;

}

 

extend(Man,Person);

 

Man.prototype.getWife = function(){

    return this.wife;

};

    该例子不需要再像前面一样手工个设置prototypeconstructor属性,通过在类声明之后立即调用该方法(在向prototype添加任何属性之前,这个顺序很重要),唯一的问题是超类(Person)的名字被固定在了Man里。更好的做法是像下面这样来引用父类:

 

function extend(subClass, superClass){

    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;//确保父类构造函数正确设置。

}

}

 

    相对与第一个版本的extended函数,它多了最后3行代码,这几行代码的主要作用是确保超类的constructor属性被正确设置,在用这个新的superClass属性调用超类的构造函数时这个问题很重要:

/**

 * Man

 * @param {Object} name

 * @param {Object} wife

 */

function Man(name, wife){

    Man.superclass.constructor.call(this,name);//这里必须确保父类的constructor属性得到正确设置。

    this.wife = wife;

}

extend(Man, Person);

Man.prototype.getWife = function(){

    return this.wife;

};

有了superclass属性,就可以直接诶调用超类中的方法,这在想重新定义超类的某个方法而又想访问超类的实现时可以派上用场,例如上面的例子中,想在Man中重新定义Person类中的同名方法时,可以这么做:

Man.prototype.getName=function(){

    var name=Man.superclass.getName.call(this);

    return name+",Hello World!";

}

 

结语

         JavaScript类式继承的实现并不复杂,但其原理却不简单,需要对原型链有一定的理解,并对函数的执行环境有所理解。由于时间所限,这里并不多做解说,有兴趣的兄弟可以自己查找并参考相关资料。

分享到:
评论

相关推荐

    学习javascript面向对象 javascript实现继承的方式

    本文实例为大家介绍了javascript实现继承的6种方式,分享给大家供大家参考,具体内容如下 1、【原型链继承】实现的本质是重写原型对象,代之以一个新类型的实例。实际上不是SubType的原型的constructor属性被重写了...

    javascript面向对象三大特征之继承实例详解

    本文实例讲述了javascript面向对象三大特征之继承。分享给大家供大家参考,具体如下: 继承 在JavaScript中的继承的实质就是子代可以拥有父代公开的一些属性和方法,在js编程时,我们一般将相同的属性放到父类中,...

    javascript面向对象三大特征之多态实例详解

    本文实例讲述了javascript面向对象三大特征之多态。分享给大家供大家参考,具体如下: 多态 从某种意义上来说,多态是面向对象中重要的一部分,也是实施继承的主要目的。 一个实例可以拥有多个类型,它既可以是这种...

    JavaScript面向对象三个基本特征实例详解【封装、继承与多态】

    本文实例讲述了JavaScript面向对象三个基本特征。分享给大家供大家参考,具体如下: 了解过面向对象的同学应该都知道,面向对象三个基本特征是:封装、继承、多态,但是对于这三个词具体可能不太了解。对于前端来讲...

    JavaScript面向对象的程序设计(犯迷糊的小羊)

    本章开始进入JavaScript核心知识点的高级部分——面向对象的程序设计,这一部分的内容将会对对象这一数据类型做进一步的深化理解,并且讲述几种创建对象的设计模式以及JavaScript独特的继承机制; 1.理解对象和面向...

    写给大家看的面向对象编程书(第3版).[美]Matt Weisfeld(带详细书签).pdf

    1.2 从过程式开发转向面向对象开发 4 1.2.1 过程式程序设计 5 1.2.2 OO程序设计 5 1.3 对象到底是什么 5 1.3.1 对象数据 5 1.3.2 对象行为 6 1.4 类到底是什么 9 1.4.1 类是对象模板 9 1.4.2 属性 11 ...

    不用构造函数(Constructor)new关键字也能实现JavaScript的面向对象

    那是因为JavaScript试图去隐藏它传统的面向对象的特性——最终导致了它的双重人格(译者注:作者意思是JavaScript既有面向过程的特征,又有面向对象的特征)。 我认为正是由于JavaScript对象模型的难以理解和使用,...

    Purely-functional-Object-Oriented-System:纯功能的面向对象系统——目前的代码实现了一个无类的、基于委托的 OO 系统,类似于 Self 或 Javascript 的系统。 这是一个成熟的 OO 系统,具有封装、对象标识、继承和多态性。 它也是一个纯粹的功能系统

    站点索引页面声明“除非另有说明,本站点上的所有代码和文档均属于公共领域”纯功能的面向对象系统目前的代码实现了一个无类的、基于委托的 OO 系统,类似于 Self 或 Javascript 的系统。 这是一个成熟的 OO 系统,...

    玩转JavaScript OOP – 类的实现详解

    对象和类是面向对象的基础,封装、继承和多态是面向对象编程的三大特性。 JavaScript提供了对象却缺乏类,它不能像C#一样能显式地定义一个类。 但是JavaScript的函数功能非常灵活,其中之一就是构造函数,结合构造...

    JS Pro-深入面向对象的程序设计之继承的详解

    一般的面向对象程序语言,有两种继承方法——接口继承(interface inheritance)和实现继承(implementation inheritance)。接口继承只继承方法签名,而实现继承则继承实际的方法。在JavaScript中,函数没有签名,...

    阿里巴巴技术文章分享 Javascript继承机制的实现

    Javascript作为一门脚本语言,在设计之初并没有考虑到面向对象的特性。即便到了当今这个遍布现代浏览器的年代,各种Javascript 框架/库如雨后春笋般地疯狂生长,Javascript中连个 class 关键字都没有。如果你要编写...

    深入理解javascript原型链和继承

    javascript本身不是面向对象的语言,而是基于对象的语言,对于习惯了其他OO语言的人来说,起初有些不适应,因为在这里没有“类”的概念,或者说“类”和“实例”不区分,更不要指望有“父类”、“子类”之分了。...

    Javascript OOP之面向对象

    一般面向对象包含:继承,封装,多态,抽象 对象形式的继承 浅拷贝 var Person = { name: 'allin', age: 18, address: { home: 'home', office: 'office', } sclools: ['x','z'], }; var programer

    worldwindjava源码-javascript-oop:面向对象编程和面向对象JavaScript

    中的面向对象编程 学习目标 使用构造函数生成特定类型的对象。 使用构造函数将属性附加到新对象。 回想一下在构造函数中定义方法的成本。 通过将自定义对象附加到原型来定义自定义对象的方法。 使用 ES6 类语法重构...

    ExtJS4中文教程2 开发笔记 chm

    Javascript 面向对象之非构造函数的继承 JavaScript对象与继承教程之内置对象(下) JavaScript对象及继承教程(上) javascript正则表达式(一) javascript正则表达式(二) JavaScript的10种跨域共享方法 JavaScript...

    tangramjs:在 JavaScript 中用于无类面向对象编程的库

    七巧板一个用于 JavaScript 中无类面向对象编程的库。 我曾经认为 JavaScript 的重要创新是原型继承。 我现在认为它是无类的面向对象编程。 我认为这是 JavaScript 给人类的礼物。 这就是使它真正有趣、特别和重要的...

    Javascript

    , 作者Douglas Crockford(他被很多开发社区认为是JavaScript专家)提出了足够多的好想法,让JavaScript成为一个杰出的面向对象编程语言。不幸的是,这些好想法(比如函数、弱类型、动态对象和表达能力很强的对象文字...

    JavaScript

    , 作者Douglas Crockford(他被很多开发社区认为是JavaScript专家)提出了足够多的好想法,让JavaScript成为一个杰出的面向对象编程语言。不幸的是,这些好想法(比如函数、弱类型、动态对象和表达能力很强的对象文字...

    javascript

    , 作者Douglas Crockford(他被很多开发社区认为是JavaScript专家)提出了足够多的好想法,让JavaScript成为一个杰出的面向对象编程语言。不幸的是,这些好想法(比如函数、弱类型、动态对象和表达能力很强的对象文字...

Global site tag (gtag.js) - Google Analytics