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

开发 前端
本文主要讲述一些Javascript面向对象的基础以及接口和继承类的实现。作者结合Javascript来设计前台方面的“设计模式”,以对后台“设计模式”做个补充。

在开始设计模式的书写之前,有必要对Javascript面向对象的概念先做个介绍,那么这篇文章就以面向对象基础作为起点吧。

理论知识

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

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

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

主要内容

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

function Man() {
   //  
}
Man.prototype.getNickName = function() {
    return "Leepy";
}; 

var man = new Man();
var name = man.getNickName(); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

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

function Man() {
    // 私有静态属性
    var Sex = "男";
    //私有静态方法
    function checkSex() {
        return (Sex == "男");
    }
    //私有方法
    this._getSex = function() {
        //调用私有静态方法
        if(checkSex())
            return "男";
        else
            return "女";
    }
    //私有方法
    this.getFirstName = function() { 
        return "Li";
    };
    //私有方法
    this.getLastName = function() {
        return "Ping";
    };
}
//公共方法
Man.prototype.getNickName = function() {
    return "Leepy";
};
//公共方法
Man.prototype.getFullName = function() {
    return this.getFirstName() + " " + this.getLastName();
};
//公共方法
Man.prototype.getSex = function() {
    //调用私有方法
    return this._getSex();
};
//公共静态方法
Man.say = function() {
    return "Happy new year!";
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.

这样的类是否看起来和传统的类很相似了呢?

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;
    }
} 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.

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

那么有了这样的一个基于接口的思想,我们设计Javascript的接口类的时候也需要考虑到这个规范。我先从主JS文件调用端开始说起:

var Person = new Interface("Person", [["getName", 0], ["setName", 1]]); 
  • 1.

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

function Man() 
{
    this.name = "";
    Interface.registerImplements(this, Person);
}
Man.prototype.getName = function() {
    return this.name;
};
Man.prototype.setName = function(name) {
    this.name = name;
}; 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

看到Man的构造函数里面包含

Interface.registerImplements(this, Person);

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

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

先看Interface的构造函数部分

unction 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]);
    }    
};
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.

刚才看到了var Person = new Interface("Person", [["getName", 0], ["setName", 1]]);,这里将两个参数分别保存起来;

#p#

调用方法部分:

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 + ",因为它找不到或者不匹配.");
            }
        }
    }
}; 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

刚才这句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(')')).rep
lace(/\s*/g,'');     //取得参数字符串
    try
    {
        return (paramString.length == 0 ? [] : paramString.split(','));
    }
    catch(err)
    {
        throw new Error("函数不合法!");
    }
} 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

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

Interface.js完整的代码如下:

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("函数不合法!");
    }
} 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.

好了该创建一个html页面来试试效果了:

<script type="text/javascript">
function test()
{
    var man = new Man();
    man.setName("Leepy");
    alert(man.getName());
}
</script> 

<input type="button" value="click" onclick="test();" />
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

最终结果为:"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";
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

其中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());
  • 1.
  • 2.
  • 3.

最后结果为:"Mr 周杰伦's"的弹出框。

【编辑推荐】

  1. JavaScript将成Silverlight的最大对手?
  2. 探秘IE8 JavaScript功能超乎想象
  3. JavaScript+CSS实现网页换肤功能
责任编辑:杨鹏飞 来源: 博客园
相关推荐

2023-09-27 23:28:28

Python编程

2011-05-25 10:59:26

Javascript继承

2010-10-08 09:13:15

oop模式JavaScript

2011-05-25 11:15:02

Javascript继承

2011-05-13 11:05:52

javascript

2020-10-20 08:35:34

JS基础进阶

2011-05-13 12:38:58

javascript

2011-05-13 09:58:46

javascript

2011-05-13 10:51:25

javascript

2011-05-13 11:17:18

javascript

2011-05-13 11:27:59

javascript

2018-12-14 11:30:00

JavaScript编程前端

2009-07-08 17:42:26

this属性

2021-01-28 08:34:30

Java对象定义

2016-10-11 15:42:08

2009-07-08 17:51:45

constructor

2009-07-08 17:48:18

prototype属性

2011-07-08 10:25:55

JavaScript

2020-12-24 08:36:14

JavaJava基础

2009-07-02 13:25:00

消除实现继承面向接口编程Java
点赞
收藏

51CTO技术栈公众号