• Qomo 2.0 beta1 发布说明及新功能


    ===============================================================================
    Qomolangma OpenProject v2.0


    类别    :Rich Web Client
    关键词  :JS OOP,JS Framwork, Rich Web Client,RIA,Web Component,
              DOM,DTHML,CSS,JavaScript,JScript

    项目发起:aimingoo (aim@263.net)
    项目团队:../../contributor.txt
    ================================================================================

    注:有关本次发布的一些重要信息请参见:
    http://blog.csdn.net/aimingoo/archive/2007/12/31/2005755.aspx

    一、Qomolangma 2.0 Beta1
    ============================
    Qomo的上次发布是在5个月前,此次的beta1主要包含对框架、兼容层和Builder系统的更新。有关界
    面组件库、图形库的发布,大约要到beta2或beta3才会提供。

    Beta1主要的更新包括:
     - 修改了大量代码和builder工具,实现对IE5.0的完整兼容,兼容safari v2,部分兼容opera
     - 对兼容层代码做了大量修改, 并调整了兼容层在system.js中的装载顺序
     - 新增载入第三方代码的$import2()函数
     - 实现接口委托
     - 实现类声明中的通用的特性get/setter
     - 更好的列举对象Enum()的实现,示例参考TestCase/T_Enum.html
     - 分析JavaScript源代码语法的工具ParseLite, 修改自Brendan Eich的Narcissus,性能极大优化
     - 一些小的实用工具函数:IsClass(), IsObject(), IsInterface(),HasInterface()等等
     - 高性能的模式替换对象Pattern(),示例参考TestCase/T_StringReplacePattern.html
     - 提供了一个正则表达式的性能分析工具:/Debug/TestCase/RegExpPerformanceTool.html
     - 其它的一些小的修正,以及代码注释上的增改

    下面对其中的一些较特别的更新做补充介绍。


    二、函数$import2()
    ============================
    $import2()是一个特殊的$imoprt()实现,它只适用于装载第三方代码的情况。beta1中它只在ParseLite
    的实现中被使用了,但以后的代码中将会有很多的地方用到它。它的函数描述为:
    ------------------
    function $import2(src, prepare, patch, condition);

     - src: 装入的.js的url地址
     - prepare: 在装入代码前面添加的代码,例如变量初始化等
     - patch: 在装入代码尾部添加的代码, 用于向外部(例如Qomo)传出数据
     - condition: 装入代码的条件, 不填写时为true
    ------------------

    其中src与condition参数与它们在$inline()和$import()中的用法是一致的。这里特别讲述一下prepare
    与patch。其中,prepare是简单的脚本文本,我们一般用它来初始化一些变量,以避免第三方代码包中
    的变量泄露出来,与Qomo的全局变量冲突。例如:
    ------------------
    // 第三方代码文本(文件名: test.js)
    value1 = 100;
    name = 'mySystem';
    ------------------

    如果我们直接执行它,那么Qomo的全局变量中就会多出value1和name两个变量名。如果我们用下面的方
    法来装载:
    ------------------
    $import2('test.js', 'var value1,name;', '');
    ------------------

    那么就不会有问题了。

    但接下来,我们装载的test.js又能如何使用呢?比如这个test.js有一个类,或者对象,我们该如何来
    使用它呢?这就需要用到patch这个参数了。不过在讲述它之前,先说明$import2()中对this引用的特
    殊理解。

    $import2()总是返回一个对象obj,如果this没有指向window,则使用this值作为obj;否则为obj新
    建一个对象实例。因此,如果你试图传入一个对象并用它传出值,那么可以这样写:
    ------------------
    var obj = {};
    $import2.call(obj, 'test.js', ...
    ------------------

    这样的技巧可以用来修改对象的原型,例如:
    ------------------
    $import2.call(MyObject.prototype, 'test.js', ...
    ------------------

    当然你也可以直接使用传出对象,而无需事先声明它。例如:
    ------------------
    var obj = $import2('test.js', ...
    ------------------

    然而这个对象(或this引用)传入之后该如何使用呢?一方面你可以为patch参数传入一个字符串做代
    码块,这个代码块中可以直接使用this。另一方面,你也可以为patch参数传入一个函数,该函数在执
    行时的第一个参数就是这个this引用。例如:
    ------------------
    var obj = $import2('test.js', 'var value1,name;', function(_this) {
      _this.value = value1;
      _this.name = name;
    });
    ------------------

    这样一来,我们就可以在外部系统中得到这个对象obj,它的value、name则来自于第三方代码。当然,
    其它的对象方法或类都可以用这种技术来从第三方代码中获取。

    $import2()的一个实现实例,是Common/ParseLite.js。它从3rd/jsparse.js中获取了parse()和tokens()
    两个方法。而这两个方法被用作ParserLite.prototype原型方法。这样一来,ParserLite()就既具有Qomo
    的类的特性,又在对Qomo完全无扰的情况下,使用了第三方代码。

    三、通用的特性get/setter
    ============================

    通用get/setter其实是Qomo V1中就已经具备的一个特性,只是未被公开。这个特性适用于这样的情况:
    某个类的许多特性的方法都基本一致,或者适合放在同一个函数中来实现,例如:
    ------------------
    function MyObject() {

      this.setName = function(v) {
        if (!v) v = 'normal';
        this.set(v);
      }

      this.setValue = function(v) {
        if (!v) v = 'normal';
        this.set(v);
      }

      // ...
    }
    ------------------

    那么你可以用下面的代码来完成相同的功能:
    ------------------
    function MyObject() {

      this.setValue =
      this.setName = function(v) {
        if (!v) v = 'normal';
        this.set(v);
      }

      // ...
    }
    ------------------

    也就是让setValue()与setName()使用同一个特性存取函数。

    为了让你在代码中能够区分调用来自于哪一个方法,Qomo在调用这个特性存取函数时,会多传入一个
    参数,因此你可以写出如下的代码:
    ------------------
    function MyObject() {

      this.setValue =
      this.setName = function(v, n) {
        if (!v) v = 'normal';
        this.set(v);

        ExtObject['OnChange' + n]();
      }

      // ...
    }
    ------------------

    这个例子中ExtObject是一个假设的外部对象。而在上面的特性存取函数被调用时,例如:
    ------------------
    obj.set('Name', ' - MyName');
    obj.set('Value', ' - MyValue');
    ------------------

    则下面的外部对象方法也将被调用:
    ------------------
    ExtObject.OnChangeName();
    ExtObject.OnChangeValue();
    ------------------

    当然,你也可以在这里传入参数v,或者用switch()语句来识别变量n并加以处理.

    最后,请注意一个细节,对于通常的set/getter来说,其声明方法是:
    ------------------
    this.getName = function() { ...
    this.setName = function(v) { ...
    ------------------

    而对通用的特性get/setter来说,传入的特性名是追加在其后的:
    ------------------
    this.getName = function(n) { ...
    this.setName = function(v,n) { ...
    ------------------

    不过两种方法实现的读取器的使用方法完全一致:
    ------------------
    obj.get(n);
    obj.set(n,v);
    ------------------


    四、接口委托(Delegate)
    ============================
    接口委托可能是本次(Qomo V2 Beta1)发布中最复杂的一项改动。在以前的文档中提及过它只是
    暂时未被实现,也指出了未被实现的原因:没有应用。

    在Qomo V2开发中,因为$import2的出现,我们遇到了一个问题:如果一个对象是由$import2()
    装载的代码来实现的,而该对象又声明了接口,则该接口也必须要委托给第三方代码实现。例如
    下面的代码中:
    ------------------
    // 接口声明
    IMyIntf = function() {
      this.doAction1 =
      this.doAction2 = Abstract;
    }

    // 类声明
    function MyObject() {
      Attribute(this, '3RdObject');
     
      this.doAction1 = function() {
        this.get('3RdObject').doAction1.apply(this, arguments);
      }

      this.doAction2 = function() {
        this.get('3RdObject').doAction2.apply(this, arguments);
      }

      this.Create = function() {
        // 1. 导入第三方代码
        // 2. 用第三方代码创建对象并置'3RdObject'特性
        this.set('3RdObject', aObject);
      }
    }

    // 类注册,并声明该类实现IMyIntf接口
    TMyObject = Class(TObject, 'MyObject', IMyIntf);
    ------------------

    在这个例子中,除了基本的框架之外,MyObject必须用重复的代码来实现doAction1, doAction2, ...
    等等类似的方法。事实上这些重复的代码并没多少特别的意义。所以我们将该接口的实现委托出去呢?

    在新的Qomo版本中,使用下面的类声明过程可以做到了:
    ------------------
    // 类声明
    function MyObject() {
      Attribute(this, '3RdObject');
     
      this.Create = function() {
        // 1. 导入第三方代码
        // 2. 用第三方代码创建对象并置'3RdObject'特性
        this.set('3RdObject', aObject);
      }

      function proxy(_this) {
        return _this.get('3RdObject');
      }

      Delegate(_cls().Create, [
       [proxy, ['IMyIntf']]
      ]);
    }
    ------------------
    Delegate()工具函数实现在Interface.js中,用于委托一个对象/类的接口,交由第三方实现。在这个
    例子中,它被交由一个返回第三方对象的代理函数(proxy)——当然也可以在Delegate()中声明并使用
    函数直接量。

    除了使用代理函数之外,也可以简单的使用一个对象(或对象直接量)。例如:
    ------------------
      var obj = {
        doAction1 : function() { ... },
        doAction2 : function() { ... }
      }

      Delegate(_cls().Create, [
       [obj, ['IMyIntf']]
      ]);
    ------------------

    Delegate函数声明为:
    ------------------
    Delegate(consigner, confer);
    ------------------

    其中consigner是委托者,在本例中是_cls().Create,由于_cls()是一个在类构造周期中使用的特殊函
    数,用于返回类的一个引用(本例中是TMyObject),所以事实上委托者就是TMyObject.Create。而该函数
    事实上直接指向构造器MyObject,而当一个接口被注册到构造器时,其子类和实例都将继承该接口。这
    就与在Class()中声明该类实现IMyIntf接口的语义是一致的了。

    上面函数声明中的confer是一个份委托协议。该协议总是一个数组,结构如下:
    ------------------
    [
     [proxy, [confer_item, ...]],
     [proxy, [confer_item, ...]],
     ...
    ];
    ------------------

    如前所述,其中proxy可以是代理对象或代理对象。而confer_item总是一个字符串,呈如下格式:
    ------------------
    [<*|+|->]InterfaceName[.MethodName[:AliasName]]
    ------------------

    字符串开始应当是一个修饰字符,如果缺省,则修饰字符为"+"。修饰字符的含义如下:
      * 实现指定接口,或所有接口的所有方法
      + 实现指定接口/接口的指定方法
      - 不实现指定接口/接口的指定方法

    也就是一个包含和排除的规则集。这个修饰字符有两条优先规则:
      1. 排除匹配(-)优先于包含匹配(+/*)
      2. 指定匹配(接口.名称)优先于通用匹配( * )

    这样,就包含了所有的委托关系。不在委托关系中的接口和方法,则默认由consigner自己来实现。

    这个字符串其它的三个部分用于指定接口。包括:
      InterfaceName : 接口名. 例如IInterface
      MethodName : 方法名. 例如IInterface.QueryInterface
      AliasName : 别名, 这是指实现者(proxy)中用来实现MethodName的名字.

    如果MethodName缺省,则表明实现整个接口(的全部方法);如果AliasName缺省,则表明代理与
    委托者使用相同的方法名。

    对于类和构造器来说,委托关系与接口注册一样,也是可以被继承的。也就是说,一旦接口被声明
    委托,则子类和对象实例都使用该委托关系来实现接口。另外,委托关系也是可以被覆盖的,这种
    覆盖是confer_item的,也就是说可以可选地覆盖先前协议中的某些部分。下面的例子说明这种继
    承与覆盖的关系。
    ------------------
    function MyObject() {
      Delegate(_cls().Create, [
       [{
         doAction1: function() { alert('doAction1') },
         doAction2: function() { alert('doAction2') }
        }, ['IMyIntf']]
      ]);
    }

    function MyObjectEx(){
      Delegate(_cls().Create, [
       [{
         doAction2: function() { alert('doAction2 - MyObjectEx') }
        }, ['IMyIntf.doAction2']]
      ]);
    }

    TMyObject = Class(TObject, 'MyObject', IMyIntf);
    TMyObjectEx = Class(TMyObject, 'MyObjectEx');

    // 测试代码
    obj1 = new MyObject();
    obj2 = new MyObjectEx();

    intf1 = QueryInterface(obj1, IMyIntf);
    intf2 = QueryInterface(obj2, IMyIntf);

    intf1.doAction2();
    intf2.doAction2();
    ------------------

    需要注意的是,接口被委托实现并不表明对象也具有该方法。以上例来讲,intf1.doAction2()调用
    成功,并不表明obj1.doAction2()也能调用成功。Qomo没有自动实现该对象方法的能力。

    五、接口内部聚合(Aggregate)
    ============================

    Aggregate在Qomo V2中有一种新的实现方案。Aggregate(聚合)的概念,是将多个接口实现在同一个
    目标中。它在Qomo的早期版本中被用于构造器或函数的内部,用于表明该构造器或函数内部实现了
    某个接口,而在它的外(方法)中不被表现出来。它的用法如下:
    ------------------
    function MyObject() {
      // 声明聚合
      var intfs = Aggregate(cls().Create, IMyIntf, IObject, ...);

      // 实现被聚合的各个接口
      var intf1 = intfs.GetInterface(IMyIntf);
      intf1.doAction1 = ...
      intf1.doAction2 = ...

      var intf2 = intfs.GetInterface(IObject);
      intf1.hasEvent = ...
      intf1.hasProperty = ...
      ...
    }

    // 声明MyObject()注册了某些接口
    Interface.RegisterInterface(MyObject, IMyIntf, IObject, ...);
    ------------------

    在Qomo V2中,在我们实现了Delegate之后,我们发现聚合在实质上也可以理解为一种委托。对于上例
    来讲,我们可以用同样的委托代码来实现:
    ------------------
    function MyObject() {
      Delegate(_cls().Create, [
       [{doAction1: ...,
         doAction2: ... }, ['IMyIntf']],
       [{hasEvent: ...,
         hasProperty: ...}, ['IObject']],
       ...
      ]);
    }
    ------------------

    因此,我们重新实现了Aggregate()这个工具函数,在它的内部存在一个名为Interfaces()的构造器。
    我们将Aggregate()的关系委托给该构造器的实例,并注册到接口系统中。这种新的实现方案比早期
    的版本更加简单,但使用的方法仍然是一致——不必再重写以前的代码。

    新的Aggregate()的实现方案演示了Delegate()的灵活应用:我们对Aggregate参数构造了一个代理对
    象,并与该代理对象(自动地)创建了一份协议。不过,为此我们也稍稍地扩展了一下Delegate(),
    使得confer_item中直接InterfaceHandle,也就是接口的内部句柄。因此,在不知道InterfaceName
    的情况下,在Delegate()中也可以将InterfaceName填为一个整数值(InterfaceHandle)。不过目前
    这只被用在Inerface.js内部,用于Aggregate的实现。


    六、其它
    ============================

    1. 关于IE5、safari和Opera的兼容
    ---------
    在Qomo V2中将全面兼容IE5.0,不过你必须使用Builer生成一个兼容它的版本,而不能直接在IE5.0中
    直接装载(未经编译的版本)和调试。

    在Qomo V2中也完全兼容safari v2,需要留意的是,对于safari的早期版本并不兼容。这是因为safari
    从v2才开始支持Function.caller。

    是同样的原因,却导致我们不能完全兼容Opera:它不支持Function.caller。而我们经过讨论,也无法
    在Opera上模拟出该效果,所以我们完成了除该项特性之外的全部兼容代码。然后,开始期待Opera的新
    版本……


    2. 关于parse分析的问题与应用
    ---------
    Qomo V2的Common目录中新增了一个ParseLite.js,我们通过装载第三方项目Narcissus的代码,并为它
    简单地注册了一个Qomo类,从而实现了这个工具。

    不过,有两点要加以说明。其一,这是一个优化过的Narcissus项目,原来的Narcissus在处理稍大的代
    码块时效率就迅速地级数下降,通过灵活地使用RegExp,Qomo避免了这些问题。以对150K源代码进行分
    析为例,Narcissus需要450秒,而Qomo的ParseLite只需要~~EN~~不到8秒。

    其二,ParseLite使用一个被裁剪过的Narcissus代码。没有execute部分,也去掉了一些扩展语法和异
    常处理——ParseLite希望被分析的代码是没有语法错误。

    所以ParseLite适合于一些特殊的语法分析,例如分析一个代码块中有多少个函数,或者一个特定片断
    中的某个符号是何种语义。尽管它最终也返回一个语法树,但这个语法树不能直接用于Execute——它
    少了一些东西。

    关于ParseLite的使用,请参考Framework/TestCase/T_ParserLite.html


    3. 接口对特性提供直接支持
    ---------
    这是一个非常好的特性。在以前我们曾经声明过这样的接口:
    ------------------
    INamedEnumer = function() {
      this.getLength =
      this.items =
      this.names = Abstract;
    }
    ------------------

    但它的实现却相当麻烦,因为Qomo对名为"getXXXXXXXXX"的方法有限制,因此你必须这样写代码:
    ------------------
    function MyObject() {
      this.getLength = function() {
        // 这里的代码是为Attribute写的
      }

      this.Create = function() {
        // 这里的代码才提供给接口, 并通过访问Attribute才得到值
        this.getLength = function() {
          return this.get('Length');
        }
      }
    }
    ------------------

    在Qomo V2中,这个过程变得非常简单。对于接口来说,在QueryInterface()时,名字以"get/set"开
    始方法名,将会被映射到"对象.get()"或"对象.set()"。除非对象自己实现了getLength,或者根本没
    有get/set方法(例如不是Qomo对象)。因此,用户不需要为此多写任意一行代码。如下例:
    ------------------
    IUserInfo = function() {
      this.getName = Abstract;
      this.getAge = Abstract;
    }

    function MyObject() {
      Attribute(this, 'Name', 'MyName');
      Attribute(this, 'Age', 30);
    }
    TMyObject = Class(TObject, 'MyObject', IUserInfo);

    var obj = new MyObjet();
    var intf = QueryInterface(obj, IUserInfo);

    alert(intf.getName());
    alert(intf.getAge());
    ------------------ 

    Qomo V2.0 Beta 1下载
    ----------
    http://groups.google.com/group/qomo/web/Qomo.V2.b1.zip

    或从如下地址签出SVN:
    ----------

    https://qomo.svn.sourceforge.net/svnroot/qomo/trunk

  • 相关阅读:
    spring的控制器如何传递数据到视图
    spring的控制器如何获取参数
    thymeleaf的官方网站
    如何编写spring mvc 项目
    如何创建spring web 工程(maven工程)
    如何下载spring sts
    使用请求包装器RequestWrapper 对博客内容进行编码
    使用apache-commons-lang3架构对HTML内容进行编码和反编码
    (转)Nginx+Php-fpm运行原理详解
    (转)FastCgi与PHP-fpm之间是个什么样的关系
  • 原文地址:https://www.cnblogs.com/encounter/p/2188666.html
Copyright © 2020-2023  润新知