`

Javascript面向对象基础以及接口和继承类的实现

阅读更多
http://developer.51cto.com/art/200901/104775.htm
在开始设计模式的书写之前,有必要对Javascript面向对象的概念先做个介绍,那么这篇文章就以面向对象基础作为起点吧。

理论知识

1. 首先Javascript是弱类型语言,它定义变量时不必声明类型,如var Person = new Person(),它的变量类型为“var”,现在的C# 3.0也引进了这种匿名类型的概念,弱类型的变量产生了极大的灵活性,因为Javascript会根据需要来进行类型转换。所以这也决定了它采用了晚绑定的方法,即在运行后才知道变量的类型;

2. 面向对象概念不必多说,封装,继承,多态;

3. Javascript对象的类型主要分为三种:本地对象,如String,Array,Date等;内置对象,如Global,Math等;宿主对象,是指传统面向对象程序设计中的作用域,如公有,保护,私有,静态等等。

主要内容

1. 现在让我们来看看Javascript怎样创建对象的:

<script type="text/javascript">
        function Man(){ }
        Man.prototype.getNickName=function()
        {
            return '爱在Cookies';
        }
        var man = new Man();
        alert(man.getNickName());
    </script>

这样就创建了最简单的类和对象,其中我们可以把function Man() {} 看作是Man类的构造函数,getNickName()看作是Man类的方法,准确说可以“当作”是Man类的公共方法;为什么要说是当作呢?那是因为其实Javascript实际上并没有一个私有共有的划分,因此开发者们自己指定了这样的规约,那么规约是什么样的呢?我这里把Man类的清单完整地列出来:

<script type="text/javascript">
      function Man()
      {
          // 私有静态属性
          var sex='男';
          //私有静态方法
          function checkSex()
          {
              return (sex=='男');
          }
        //私有方法
          this._getSex=function()
          {
             if(checkSex())
             {
                 return '男';
             }else
             {
                 return '女';
             }
          }
          //私有方法
          this.getFirstName=function()
          {
              return '陈';
          }
          //私有方法
          this.getLastName=function()
          {
              return '顶强';
          }
      }
        //公共方法      
       Man.prototype.getNickName=function()
       {
           return '爱在Cookies';
       }
        //公共方法
        Man.prototype.getFullName=function()
        {
            return this.getFirstName+this.getLastName;
        }
         //公共方法
        Man.prototype.getSex=function()
        {
            return this._getSex();
        }
        //公共静态方法         
        Man.say=function()
        {
            return 'Happy new Year!';
        }
    </script>

这样的类是否看起来和传统的类很相似了呢?
2.接下来这个是本篇的一个重点,就是用Javascript如何设计一个接口,然后让类继承于它。

首先,先让我们看传统的C#语言是如何设计接口的吧:
public interface Person
{
    string GetName();
    void SetName(string name);
}
public class Man : Person
{
    private string _name; 

    public string GetName()
    {
        return _name;
    }
    public void SetName(string name)
    {
        _name = name;
    }
} 

接口中可以声明属性、方法、事件和类型(Structure),(但不能声明变量),但是并不能设置这些成员的具体值,也就是说,只能定义,不能给它里面定义的东西赋值,而接口作为它的继承类或者派生类的规约,继承类或者它的派生类能够共同完成接口属性、方法、事件和类型的具体实现,因为这里GetName(),SetName(),不管是方法名还是属性调用顺序上都是要保持一致的;

那么有了这样的一个基于接口的思想,我们设计Javascript的接口类的时候也需要考虑到这个规范。我先从主JS文件调用端开始说起:
 <script type="text/javascript">
     function Man()
    {
        this.name = "";
        Interface.registerImplements(this, Person);
    }
    Man.prototype.getName = function() {
        return this.name;
    };
    Man.prototype.setName = function(name) {
        this.name = name;
    };
    </script>

其中Interface类是稍后要说的接口类,第一个参数"Person"是接口类的名称,第二个参数是个二维数组,"getName"是接口方法的名称,"0"是该方法所带的参数个数(因为Javascript是弱语言,所以类型是不确定的,所以只要记住参数个数就好,"0"可以省略不写),"setName"同理。这样一个接口定义好了。怎样使用它呢?
看到Man的构造函数里面包含

Interface.registerImplements(this, Person);

它是用来将实例化的this对象继承于Person接口,然后继承类对接口的方法进行实现。

代码看起来是不是很清晰和简单呢,那么现在要开始介绍真正的核心代码Interface.js了:

先看Interface的构造函数部分

 <script type="text/javascript">

         function  Interface(name,methods)
         {
               if(arguments.length!=2)
               {
                   throw new Error("接口构造函数含" + arguments.length + "个参数, 但需要2个参数.");
               }
             this.name=name;
             this.methods = [];
             if(this.methods.length<1)
             {
                 throw new Error('第二个参数为空数组.');
             }
             for(var i=0,len=methods.length;i<len;i++)
             {
                 if(typeof methods[i][0] !== 'string')
                 {
                     throw new Error("接口构造函数第一个参数必须为字符串类型.");                     
                 }
                 if(methods[i][0] && typeof methods[i][1] !== 'number')
                 {
                     throw new Error("接口构造函数第二个参数必须为整数类型.");                     
                 }
                 if(methods[i].length=1)
                 {
                     methods[i][1]=0;
                 }
                 this.methods.push(methods[i]);
             }
         }

        </script>


刚才看到了var Person = new Interface("Person", [["getName", 0], ["setName", 1]]);,这里将两个参数分别保存起来;
  Interface.registerImplements=function(object)
         {
               if(arguments.length<2)
               {
                   throw new Error("接口的实现必须包含至少2个参数.");
               }
             for(var i=1,len=arguments.length;i<len;i++)
             {
                 var interface=arguments[i];
                 if(interface.constructor != Interface )
                 {
                     throw new Error("从第2个以上的参数必须为接口实例.");                     
                 }
                 for(var j = 0, methodsLen = interface.methods.length; j < methodsLen; j++)
                 {
                     var method = interface.methods[j][0];
                     if(!object[method] || typeof object[method] !== 'function' || object[method].getParameters().length != interface.methods[j][1])
                     {
                         throw new Error("接口的实现对象不能执行" + interface.name + "的接口方法" + method + ",因为它找不到或者不匹配.");
                     }
                  }
             }
         }

刚才这句Interface.registerImplements(this, Person);,实际上这里是把this对象的方法名以及参数个数与刚Person保存的methods逐一进行比较,如果找不到或者不匹配,就警告错误;其中object[method].getParameters().length,调用了如下的代码:
  Function.prototype.getParameters=function()
        {
            var str = this.toString();
            var paramString = str.slice(str.indexOf('(') + 1, str.indexOf(')')).replace(/\s*/g,'');     //取得参数字符串
            try
            {
                return (paramString.length == 0 ? [] : paramString.split(','));
            }
            catch(err)
            {
                throw new Error("函数不合法!");
            }

        }

getParrameters()方法作为Function对象的一个扩展,功能是取得方法含有的参数数组;

Interface.js完整的代码如下:

function Interface(name, methods) 
{
    if(arguments.length != 2) {
        throw new Error("接口构造函数含" + arguments.length + "个参数, 但需要2个参数.");
    }
    this.name = name;
    this.methods = [];
    if(methods.length < 1) {
        throw new Error("第二个参数为空数组.");
    }
    for(var i = 0, len = methods.length; i < len; i++) {
        if(typeof methods[i][0] !== 'string') {
            throw new Error("接口构造函数第一个参数必须为字符串类型.");
        }
        if(methods[i][1] && typeof methods[i][1] !== 'number') {
            throw new Error("接口构造函数第二个参数必须为整数类型.");
        }
        if(methods[i].length == 1) {
            methods[i][1] = 0;
        } 

        this.methods.push(methods[i]);
    }    
}; 

Interface.registerImplements = function(object) { 

    if(arguments.length < 2) {
        throw new Error("接口的实现必须包含至少2个参数.");
    } 

    for(var i = 1, len = arguments.length; i < len; i++) {
        var interface = arguments[i];
        if(interface.constructor !== Interface) {
            throw new Error("从第2个以上的参数必须为接口实例.");
        }
        for(var j = 0, methodsLen = interface.methods.length; j < methodsLen; j++) {
            var method = interface.methods[j][0];
            if(!object[method] || typeof object[method] !== 'function' || object[method].getParameters().length != interface.methods[j][1]) {
                throw new Error("接口的实现对象不能执行" + interface.name + "的接口方法" + method + ",因为它找不到或者不匹配.");
            }
        }
    }
}; 

Function.prototype.getParameters = function() { 

    var str = this.toString();
    var paramString = str.slice(str.indexOf('(') + 1, str.indexOf(')')).replace(/\s*/g,'');     //取得参数字符串
    try
    {
        return (paramString.length == 0 ? [] : paramString.split(','));
    }
    catch(err)
    {
        throw new Error("函数不合法!");
    }
} 

好了该创建一个html页面来试试效果了:
最终结果为:"Leepy"的弹出框。

这里还有一点要强调,如果接口上的方法没有在继承类上得到完全实现,或者方法参数个数不匹配,那么就会提示错误。

3. 如果我要一个类继承于另一个类该怎么做呢,继续看例子,这里我再定义一个SchoolBoy(男学生)类:
function SchoolBoy(classNo, post)
{
    Man.call(this);
    this._chassNo = classNo;
    this._post = post;
}
SchoolBoy.prototype = new Man();
SchoolBoy.prototype.getName = function() {
    return "Mr " + this.name;
}
SchoolBoy.prototype.setName = function(name) {
    this.name = name + "'s";
}

其中Man.call(this);实际上是将Man中的关键字this赋值于SchoolBoy对象中去,那么SchoolBoy就拥有了Man构造函数中的name属性了。

SchoolBoy.prototype = new Man();实际上是把Man的prototype赋值给SchoolBoy.prototype,那么SchoolBoy就有了Man类中的方法。

而后面跟着的getName(),setName(),实际上是覆盖了前面继承于Man类中的方法了。

然后看看效果:
var schoolboy = new SchoolBoy("三年二班", "班长");
schoolboy.setName("周杰伦");
alert(schoolboy.getName());

最后结果为:"Mr 周杰伦's"的弹出框。
分享到:
评论

相关推荐

    面向对象JavaScript精要(英文原版pdf)

    本书全面覆盖了面向对象编程的基础理论,并结合JavaScript的具体实现进行讲解。 - **第一章:JavaScript简介**:介绍JavaScript的发展历程、特点及其与Web浏览器的关系。 - **第二章:面向对象编程简介**:解释OOP...

    Javascript面向对象基础.rar

    在这个“JavaScript面向对象基础”的资料中,我们将会探讨JavaScript中的类、对象、封装、继承以及多态等关键概念。 1. **对象与对象字面量** 在JavaScript中,对象是由键值对组成的无序集合,可以使用对象字面量...

    Javascript面向对象基础

    下面我们将详细探讨JavaScript面向对象的基础知识。 1. **对象和数据类型**: 在JavaScript中,一切都是对象,包括基本数据类型(如字符串、数字、布尔值)。然而,与数字或字符串不同,对象是键值对的集合,可以...

    Javascript 面向对象的JavaScript进阶

    ### JavaScript面向对象进阶知识点详解 #### 8.1 面向对象编程的基本特性 在探讨面向对象的JavaScript之前,我们首先需要了解面向对象编程(Object-Oriented Programming, OOP)的基本特性:封装性、抽象性、继承...

    JavaScript面向对象编程指南.pdf

    通过以上内容,我们可以了解到JavaScript面向对象编程的基础知识以及它的核心概念。在实际应用中,理解并熟练运用这些知识点,将有助于开发出更加结构化和可维护的代码。同时,参与Java学习群和听讲大神的课程将是一...

    JavaScript面向对象编程指南

    JavaScript是一种广泛应用于Web开发的动态编程语言,尤其在构建交互式网页和富互联网应用...《JavaScript面向对象编程指南》这本书将帮助你进一步探索这个主题,通过实例和详细的解释,提升你的JavaScript编程技能。

    Javascript面向对象特性实现(封装、继承、接口).doc

    JavaScript是一种动态类型的脚本语言,虽然它不像Java或C#那样是典型的面向对象语言,但JavaScript仍然支持面向对象编程的概念,包括封装、继承和接口。下面我们将深入探讨这些概念。 **封装**是面向对象编程的基本...

    面向对象JavaScript开发

    JavaScript,作为一种广泛应用...JavaScript 面向对象程序设计——继承与多态.pdf 和 JavaScript 面向对象程序设计——封装.pdf 这两个文档可能深入探讨了这些主题,帮助读者深入理解并掌握JavaScript的面向对象开发。

    JavaScript面向对象精要(英文版)

    《JavaScript面向对象精要》这本书不仅介绍了JavaScript面向对象的基础概念,还深入探讨了其实现机制及其在实际开发中的应用。对于希望提高自己JavaScript技能水平的开发者来说,本书是一本不可多得的好书。通过学习...

    javascript面向对象

    本文旨在深入解析JavaScript面向对象编程的基础,包括对象的基本概念、类与对象的关系、对象的属性与方法、封装、继承与多态,以及JavaScript中对象的实现方式。 #### 对象的基本概念 面向对象编程的核心在于对...

    JavaScript面向对象编程指南 pdf

    下面将详细探讨JavaScript面向对象编程的基本原理、特性以及实际应用。 1. **类与对象** - 在JavaScript中,对象是键值对的集合,可以通过字面量语法或构造函数创建。例如: ```javascript const person = { ...

    javascript面向对象编程pdf

    总结来说,JavaScript面向对象编程涵盖了从基本原理到实现细节的多个方面,包括但不限于对象、构造函数、原型链、继承以及封装等。本书系统地介绍了这些知识点,并通过实例演示了如何在现代JavaScript中实现OOP,...

    JavaScript面向对象高级编程

    在当今的软件开发中,JavaScript语言因其在Web前端开发中的广泛应用而变得极其重要。...通过理解这些概念和实践知识,开发者可以在进行JavaScript面向对象高级编程时,设计出更加合理、易于维护和扩展的应用程序。

    javascript 经典面向对象设计

    《Object-Oriented JavaScript》这本书不仅深入介绍了面向对象编程的基础理论,还提供了大量的实际代码示例和最佳实践指导。对于希望提高JavaScript开发技能、编写出高质量、可扩展和可重用代码的开发者来说,这本书...

    JavaScript面向对象技术实现树形控件

    JavaScript面向对象技术在实现树形控件中扮演着至关重要的角色。树形控件是一种用于展示层次型数据的用户界面元素,它具有扩展和折叠功能,可以在有限的空间内清晰地展示大量的信息,使得数据间的层级关系一目了然。...

Global site tag (gtag.js) - Google Analytics