首页 > 代码库 > javascript面向对象

javascript面向对象

题目有点大了-.-。

好久没写博客了,这段时间忽然找不到主题了。最近刚刚给自己重构了一下前端表单的功能,通过各处借鉴,并利用了面向对象的方式,有一点点代码,所以拿出来分享一下。

javascript面向对象的方式,我也是通过博客园学了一些,如果你还不是很清楚,可以先看了解一下javascript的Function,Function.prototype。

在写之前,确定了一个主要思路,先写测试。为了图简便,先写了一个简单的单元测试功能,而后开始实现了名称空间,类,类的继承等几项功能。

单元测试功能

主要包括一个测试方法:

 

test(待测试方法,测试结果)
test(待测试方法,测试结果方法)

例:

function getSum(a,b){

  return a + b;

}

test("getSum",getSum(1,2) == 3);

test("getSum",function(msgWrite){

      msgWrite("1+2=?");

  return getSum(1,2) == 3

});

实现很简单,先贴上代码,各位看官有兴趣自己看咯。

(function (window, body) {    var trp = window.document.createElement("div");    if (body) {        body.appendChild(trp);    }    else {        window.addEventListener("load", function () {            window.document.body.appendChild(trp);        });    }    function writeResult(panel, msg) {        var result = window.document.createElement("div");        result.innerHTML = encodeHTML(msg);        panel.appendChild(result);    }    window.test = function (item, arg) {        var itemPanel = window.document.createElement("div");        itemPanel.innerHTML = "<p>" + encodeHTML(item) + "</p>";        trp.appendChild(itemPanel);        var msgPanel = window.document.createElement("div");        itemPanel.appendChild(msgPanel);        if (typeof (arg) == "boolean") {        }        else if (typeof (arg) == "function") {            try {                arg = arg(function (msg) {                    writeResult(msgPanel, msg);                });            } catch (e) {                arg = false;                writeResult(msgPanel, e);            }        }        writeResult(msgPanel, arg ? "成功" : "失败");        if (arg) {            itemPanel.style["background"] = "#0f0";        }        else {            itemPanel.style["background"] = "#f00";        }    };})(window, window.document.body);
testcore.js

将testcore.js和 功能实现js 以及单元测试js引入到一个html中,打开html即可看到单元测试结果。红色为失败,绿色为成功。

名称空间,类,类的继承的单元测试

写了单元测试,其实就代表了我们本次的需求。下面说下本次的需求。

名称空间

能够注册名称空间,能在该空间下添加类,各空间下可以注册名称相同的类。

test("框架注册", !!wod && !!wod.CLS);  

 

wod为本身核心库的名称空间

CLS为当前的所有类的基类,相当于.net的object。

test("注册名称空间", !!wod.CLS.getNS("mini.ui") && !!mini.ui && wod.CLS.getNS("mini.ui") == mini.ui);

类,类的继承

CLS同时提供核心方法

getNS("名称空间"),如果不存在名称空间,则注册并返回名称空间对象,如果存在,则返回该对象

由于最近项目上用的miniui,所以mini.ui躺枪了

    test("注册类", !!wod.CLS.getClass("mini.ui.TextBox") && !!mini.ui.TextBox);    mini.ui.TextBox.prototype.getText = function () {        return "abc";    };    test("继承类", !!wod.CLS.getClass("mini.ui.SubTextBox", "mini.ui.TextBox")        && !!mini.ui.SubTextBox
&& !!new mini.ui.SubTextBox()
&& mini.ui.SubTextBox.isSubclassOf(mini.ui.TextBox)); test("继承类-继承方法验证1", function (msgWrite) { var sub = new mini.ui.SubTextBox(); return sub.getText && sub.getText() == "abc"; }); test("继承类-继承方法验证2", function (msgWrite) { var subCLS = wod.CLS.getClass({ getText: function () { return "h" + this.parent(); }, hh: function () { return this.getText(); } }, "mini.ui.SubTextBox1", "mini.ui.SubTextBox"); var sub = new subCLS(); return sub.getText && sub.getText() == "habc" && sub.hh; });

 

 

重载了四个getClass方法:

1、getClass(className),注册一个className类(默认继承CLS),然后通过prototype给类添加属性及方法

2、getClass(attr,className),注册一个className类(默认继承CLS),将attr的属性及方法附加给className类

3、getClass(className,baseClassName),注册一个className类,继承自baseClassName,然后通过prototype给类添加属性及方法

4、getClass(attr,className,baseClassName),注册一个className类,继承自baseClassName,将attr的属性及方法附加给className类

上面的单元测试说明了本次实现的需求:

1、能注册类,这个类必须注册成功

2、注册的类,能直接通过new 关键字进行实例化,通过property能设置其属性及方法

3、注册的类包含isSubclassOf方法判断是某的类的子类

4、子类的同名方法会重写基类的方法,并在方法内部,通过this.parent(arg)来调用基类的本方法(从impactjs借鉴过来,还是很好用的,实现类似c#base.Method()的功能)

核心库的实现

测试写完了,终于要开始重头戏啦=.=。

对于javascript来说,并没有名称空间,他有的只是对象。为了让他支持名称空间,可以用对象来表示名称空间,如果该空间包括子空间,则再该对象上增加子空间的属性,就像下面一样

var System =  new Object();

System.Window = new Object();

所以我们的核心库的名称空间也像这样实现了

var wod = new Object();

 

=.=好简陋。

然后是类,所以我们的wod.CLS不能是Object啦,他必须是一个Function

wod.CLS = function(){};

 

怎么又这么简陋=.=。

在wod.CLS上增加getNS(namespace)方法,通过new Object()的方式,很容易将他实现:

        wod.CLS.getNS = function (nsName) {            var arr;            if (!nsName || ((arr = nsName.split(".")) && arr.length == 0))                return undefined;            var base = window;            for (var i = 0, length = arr.length; i < length; i++) {                if (base[arr[i]]) {                }                else {                    base[arr[i]] = new Object();                }                base = base[arr[i]];            }            return base;        };

 

然后来实现注册类:

wod.CLS.getClass = function(className,baseClassName){  var baseClass = eval("("+baseClassName+")");//这里baseClassName一定是存在的就得到基类的那个function  var construct = function(){      };//初始化一个当前的类      construct._super = baseClass;//记录他的基本是baseClass      var ns = this.getClassNS(className);//通过className获取NS      ns[className.splite(‘.‘).pop()] = construct;//将类放到NS上      return construct;};

 

这样就是一个基本雏形了。

下面需要对齐进行完善,实现isSubclassOf,继承,parent等功能。

由于我们的类是由Function实现的,所以可以增加方法

    Function.prototype.isSubclassOf = function (base) {        if (this._super == null)            return false;        if (this._super === base)            return true;        else {            return this._super.isSubclassOf(base);        }    };

 

然后是继承,我们可以创建一个baseClass的实例,并将该实例的所有属性放到当前类的prototype上,然后当前类的所有新的实例自然就有了baseClass的所有方法。

construct.prototype = new baseClass();

 

再来是构造方法,我们经常需要去定义一个类的构造方法,如new Person(personName)的形式。但是我们的注册类的实例方法中,不能自由定义,所以得有变通方法。我的方法就是增加一个_init方法,由各个子类的_init方法去实现自己的构造方法,所以construct变成了这样:

var construct = function(){    this._init.apply(this,arguments);//不管在构造函数里有几个参数,都一个不漏的传送到了_init方法中}

 

我们还要使用parent实现类似c#base.Method(),所以每一个类都有parent方法,所以在CLS中增加该方法,并通过construct._super来寻找对应的基类的方法,并调用他:

    wod.CLS = function () {        this.parent = function () {            var cls = this.constructor;            var caller = arguments.callee.caller;            var finded = false;            var result;            while (!finded) {                for (var tmp in cls.prototype) {                    if (cls.prototype[tmp] == caller) {                        if (cls._superpt[tmp] && cls._superpt[tmp] != caller) {                            result = cls._superpt[tmp].apply(this, arguments);                            finded = true;                        }                        break;                    }                }                if (!finded) {                    cls = cls._super;                }            }            return result;        };    };

 

大功几乎告成。

现在,我们可以任意定义我们的类啦,

写个例子吧:

wod.CLS.getClass({  name:"",   eat:function(food){       alert(this.name+‘ is eating ‘ + food +‘!‘);  }},‘my.eatbase‘);wod.CLS.getClass({  name:"cat"},‘my.cat‘);wod.CLS.getClass({   _init:function(name){        this.name = name||this.name;        this.parent(name);   },  name:"dog"},‘my.dog‘);new my.dog().eat(); new my.dog(‘旺福‘).eat();new my.cat().eat();

 

怎么样呢?

 

这样就结束啦。大家可以在后面的链接下载下来玩一下。

里面为了将属性优雅的附加到prototype上还用到了mixin和clone方法,可以去了解一下 clone mixin extend。之前看到的网页怎么也搜不到了.......,找到了再放上来。

在prototype中增加对象属性的时候,当使用new 关键字的时候,多个对象会共用prototype中的这一个对象属性,所以在CLS的_init方法使用了clone的方式,给不同的对象设置不同的值。这个问题正好在项目中还引起过一个BUG =.= 。

 

下载

 

javascript面向对象