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 =.= 。

下载

原文地址:https://www.cnblogs.com/xvyw/p/4082607.html