当前位置 : 主页 > 网页制作 > Dojo >

Dojo class中跟变量相关的几个问题

来源:互联网 收集:自由互联 发布时间:2021-06-15
Fire an event manually Dojo class中跟变量相关的几个问题 2011 年 11 月 09 日 | dojo 针对 Java 开发人员的 Dojo 概念是学习dojo不得不读的一篇文章。本文对该文中没有详尽讲述的的跟变量相关的几
Fire an event manually

Dojo class中跟变量相关的几个问题

  | dojo

针对 Java 开发人员的 Dojo 概念是学习dojo不得不读的一篇文章。本文对该文中没有详尽讲述的的跟变量相关的几个问题做一些阐述。

引文中在“复杂的类属性”一节,举例如下:

    dojo.declare(
    "myClass",
    null,
    {
    globalComplexArg : { val : "foo" },
    localComplexArg : null,
    constructor : function() {
    this.localComplexArg = { val:"bar" };
    }
    }
    );
     
    // Create instances of myClass A and B...
    var A = new myClass();
    var B = new myClass();
     
    // Output A's attributes...
    console.log("A's global val: " + A.globalComplexArg.val);
    console.log("A's local val: " + A.localComplexArg.val);
     
    // Update both of A's attributes...
    A.globalComplexArg.val = "updatedFoo";
    A.localComplexArg.val = "updatedBar";
     
    // Update B's attributes...
    console.log("A's global val: " + B.globalComplexArg.val);
    console.log("A's local val: " + B.localComplexArg.val);

引文中谈到,“类属性可以在声明时进行初始化,但是如果使用复杂对象类型(例如 hash 或数组)初始化属性,该属性将类似于 Java 类中的公共静态变量。这意味着任何实例无论在何时更新它,修改将反映到所有其他实例中。为了避免这个问题,应当在构造函数中初始化复杂属性;然而,对于字符串、布尔值等简单属性则不需要这样做。 ”这里需要特别强调的是,上述示例中的localComplexArg是类的属性之一,但当一个“类”实例化以后,这个属性的取值是各个实例自己拥有一份,不会共享。globalComplexArg只是类似于Java类的公共静态变量,但从其调用方法来看,仍然必须通过实例化的对象来调用,所以dojo的本意并非要将其模拟为一个类的静态变量。它的存在,更象是一个不得已的例外。

  • 在dojo类中真正要使用类的静态变量,推荐使用下面的语法:

myClass.staticVar = “xxx”;// A static property is declared. You don’t need to instantiate myClass to use this property, instead, use it directly, via the class name

  • 上述globalComplexArg并非在实例间共享,而是通过原型链共享。即便不存在任何实例,该globalComplexArg的取值仍然存在。如果从未通过类myClass实例化过任何类,则在上例中,始终拥有{val : “foo”}这个对象;如果实例化后又改变过其取值,则始终保存改变后的值,即使是后来所有的实例都消亡,这个值也不会被析构。请看下面的例子:
    dojo.declare("my.GrandPa", null, {
    print:function(msg){
    console.log("this is grandpa:", msg);
    }
    });
    dojo.declare("my.Parent", [my.GrandPa], {
    a : 0,
    b : [],
    constructor:function(){
    this.c = 110;
    this.b.push('Parent()');
    },
     
    print:function(msg){
    console.log("my.Parent.print:a=%s, b = [%s], c=%s ", this.a, this.b, this.c, msg);
    }
    });
     
    dojo.declare("my.Son", [my.Parent], {
    a : 10,
    constructor:function(){
    this.b.push('Son()');
    },
    print:function(msg){
    console.log("in my.Son ", msg);
    this.inherited(arguments);
    }
    });
     
    var g1 = new my.GrandPa();
    g1.print("from g1");
     
    var f1 = new my.Parent();
    f1.print("from f1");
    f1.a = 100;
    f1.b.push(125);
    f1 = null;
     
    var f2 = new my.Parent();
    f2.print("from f2");
    f2.a = 99;
    f2.print("from f2, after a changed");
     
    var s1 = new my.Son();
    s1.print("from s1");
    g1.print("from g1 again");

运行上例,得到输出如下:

    this is grandpa: from g1
    my.Parent.print:a=0, b = [Parent()], c=110 from f1
    my.Parent.print:a=0, b = [Parent(),125,Parent()], c=110 from f2
    my.Parent.print:a=99, b = [Parent(),125,Parent()], c=110 from f2, after a changed
    in my.Son from s1
    my.Parent.print:a=10, b = [Parent(),125,Parent(),Parent(),Son()], c=110 from s1
    dojo.xd.js (第 3306 行)
    this is grandpa: from g1 again

上述代码首先定义了一个GrandPa类,没有任何属性,只有一个方法。然后定义了Parent类,它有简单属性a和一个数组b,并在构造函数中,将构造函数的名字保存至数组b中。代码第31行比较有趣。此时f1是Parent类的惟一一个实例,通过f1= null我们将其析构。此时f1.a不再可以访问,但数组b仍然保存了我们刚刚push进去的值125,并且在我们实例化对象s1时,可以再次访问这个值。

结论:从上例我们可以发现,

  1. 在子类中声明的属性并不会成为父类的属性(my.Parent),dojo在这一点上实现了传统语言的语法。
  2. 在类声明中(与constructor对应)声明的复杂类型变量,其值保存在原型声明中,从而实际上被所有的实例(从本类实例化的对象,以及从子类实例化的对象)共享(my.Parent, my.Son)。这一点上,dojo的实现与传统语言并不一致。
  3. 子类的方法和属性可以覆盖父类(my.Son, 第20行)。
  4. dojo class并没有私有成员。这句话有两层意思,其一,可以通过实例变量直接访问任何变量(比如f1.a),其二,所有在父类中声明/定义的属性(无论是在声明中初始化:my.Parent第7行,a: 0,还是在构造函数中初始化my.Parent第10行, this.c = 110),都将成为子类的一个属性(见输出部分第6行)。
  5. 基于上面的发现,应该始终通过myClass.staticVar = “xx”来为类引入静态变量;始终将所有的类属性声明放入到构造器constructor中。这些属性仍然可以被子类继承,但不会有被错误共享的担忧。

上例中没有揭示当子类覆盖了父类的属性和方法后,是否还可以访问父类的属性和方法?当然,子类覆盖父类的属性,实质是使用不同的值进行初始化,保存父类属性的初始化值并没有意义。而要访问父类的方法,可以使用this.inherited(arguments)。注意,这是个固定的写法,不可以有任何改变。即参数一定是arguments,而且一定要带上。此外,dojo还做了一个有意义的扩展,即调用链。传统语言只在构造函数上有调用链,即当子类实例化时,会优先调用父类的构造函数,然后才是子类的构造函数。但dojo通过声明”-chains-”属性,实现了对其它方法的链式调用,并且可以指明基类函数和派生类函数的先后调用次序。

网友评论