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

    下载

  • 相关阅读:
    10:简单密码
    08:字符替换
    07:配对碱基链
    05:输出亲朋字符串
    18:等差数列末项计算
    09:密码翻译
    用最通俗的话说23种设计模式之代理模式
    Android学习之 UI效果
    精确到时分秒的jQuery插件例子
    Eclipse 常用快捷键
  • 原文地址:https://www.cnblogs.com/xvyw/p/4082607.html
Copyright © 2020-2023  润新知