说实话,在出问题之前,下面要讨论的Dojo Widget特性是我无论如何也想不到的。问题是这样的。我用一个继承自dijit.layout.ContentPane的Dojo Widget “Test”封装了一块页面内容。由于这块内容包含了很多事件句柄,我希望在每次刷新或关闭这块内容时销毁这些句柄。因此我在Test中定义了一个数组变量handles,用于储存句柄列表以便操作。不幸的是,当我的页面上存在多个
Test实例时,关闭一个Test实例导致了所有其他Test实例中事件句柄的销毁,从而它们的事件全部不能触发了。要知道这对于不知情的用户来说实在是一个致命的问题,页面上的按钮突然全都没用了,而浏览器甚至不会给任何提示。
既然问题出在Dojo Widget的变量作用域上,这儿就来研究一下。
首先构建一个测试案例:
<div>
t1: outter: <span id="t1outter">0</span><br/>
t1: oarray: <span id="t1oarray"></span><br/>
t1: oobj: <span id="t1oobj"></span><br/>
t1: inner: <span id="t1inner">0</span><br/>
t1: iarray: <span id="t1iarray"></span><br/>
t1: iobj: <span id="t1iobj"></span><br/>
<br/>
t2: outter: <span id="t2outter">0</span><br/>
t2: oarray: <span id="t2oarray"></span><br/>
t2: oobj: <span id="t2oobj"></span><br/>
t2: inner: <span id="t2inner">0</span><br/>
t2: iarray: <span id="t2iarray"></span><br/>
t2: iobj: <span id="t2iobj"></span><br/>
</div>
<div id="container" dojoType="dijit.layout.TabContainer" style="width:1000px;height:400px;">
<div id="t1" dojoType="Test" title="Tab1" region="center">
<input type="button" value="Add1" οnclick="c1();"/>
</div>
<div id="t2" dojoType="Test" title="Tab2" region="center">
<input type="button" value="Add2" οnclick="c2();"/>
</div>
</div>
这个页面上有一个dijit TabContainer,包含了两个Test实例。每个实例里面各有一个按钮,可操作相应实例中的变量。
下面是Test的定义:
dojo.declare("Test", [ dijit.layout.ContentPane ], {
outter: 0, //integer
oarray: [], //array object
oobj: {}, //object
constructor: function(){
this.inner = 0; //integer
this.iarray = [],//array object
this.iobj = {}, //object
}
});
里面定义了6个变量。
每当点击页面上的按钮时,相应实例中的变量值会被改变,并显示在页面上部。
function c1(){
var w1 = dijit.byId("t1");
var w2 = dijit.byId("t2");
w1.outter++;
w1.oarray.push(w1.outter);
w1.oobj["t1-"+w1.outter] = w1.outter;
w1.inner++;
w1.iarray.push(w1.outter);
w1.iobj["t1-"+w1.inner] = w1.inner;
this.refreshResult(w1, w2);
}
function c2(){
var w1 = dijit.byId("t1");
var w2 = dijit.byId("t2");
w2.outter++;
w2.oarray.push(w2.outter);
w2.oobj["t2-"+w2.outter] = w2.outter;
w2.inner++;
w2.iarray.push(w2.outter);
w2.iobj["t1-"+w2.inner] = w2.inner;
this.refreshResult(w1, w2);
}
function refreshResult(w1, w2){
dojo.byId("t1outter").innerHTML = w1.outter;
dojo.byId("t1oarray").innerHTML = w1.oarray;
dojo.byId("t1oobj").innerHTML = w1.oobj.tos();
dojo.byId("t1inner").innerHTML = w1.inner;
dojo.byId("t1iarray").innerHTML = w1.iarray;
dojo.byId("t1iobj").innerHTML = w1.iobj.tos();
dojo.byId("t2outter").innerHTML = w2.outter;
dojo.byId("t2oarray").innerHTML = w2.oarray;
dojo.byId("t2oobj").innerHTML = w2.oobj.tos();
dojo.byId("t2inner").innerHTML = w2.inner;
dojo.byId("t2iarray").innerHTML = w2.iarray;
dojo.byId("t2iobj").innerHTML = w2.iobj.tos();
}
准备完毕,下面就来看看页面上会发生什么。
初始界面:
点击按钮Add1 2次后的结果:
接着点击按钮Add2 4次后的结果:
从最后一个图中可以很清楚地看到,当操作一个Test实例的变量oarray和oobj时,会同时作用在其他Test实例的变量oarray和oobj上!当我将变量handles定义成oarray及oobj这样的时,开头提到的致命问题就出现了。
结论:
1. 当变量类型既不是JavaScript object,也不是JavaScript function时,放那儿作用域都一样,都只能被当前实例操作。比如上面的outter和inner。
2. 当变量是JavaScript object或JavaScript function类型时,就要小心了。如果只想让它被当前实例操作,就放到constructor中(估计也能放在其他function比如startup中,没具体测试过),比如iarray和iobj。如果想跨实例作用,则大胆地放在实例中function的外面,如oarray和oobj。