虚拟面向对象
由于JavaScript本身并不支持面向对象,但是我们可以利用其Prototype去模拟部分面向对象的语言特征。我们这儿要说的虚拟面向对象就是指Dorado中提供的JavaScript模拟面向对象的编程技术。 在基础教程中我们只介绍Dorado JS对象的使用和相关约定,而忽略对象的声明、扩展等内容。
虚拟属性
我们使用Dorado提供的JSDOC的时候注意到,其中Dorado的JS对象通常都包含很多的attributes,如我们访问URL: http://dorado.bstek.com/jsdoc/ 访问dorado.widget下的Button控件: 这些attributes就是Dorado对象的虚拟属性,之所以称它们为虚拟属性是,因为它们与Java中的属性不同的地方在于,Java中的属性我们都可以通过专用的getXXX/setXXX方法进行读取操作,但是Dorado对象的虚拟属性我们需要通过如下的方式:
var v = editor.get("value");
button.set("caption", "OK");
也就是说当我们看到JSDOC中某一个Dorado控件具有的某些attributes,我们就可以通过get/set方法,例如刚才我们看到的Button中有icon属性,则我们就可以通过如下的方式设置其icon属性:
button.set("icon", "images/button_ok.gif");
下面介绍虚拟属性的一些其他特色: set方法支持批量属性设置 如下代码,我们对一个对象的多个属性进行赋值操作:
button.set("icon", "images/ok.gif");
button.set("caption", "OK");
button.onClick = function(){
alert("You clicked OK.");
}
这种代码有些罗嗦,尤其在属性较多情况下更是如此,有什么简化的办法呢? 虚拟属性的set方法支持属性批量设置,如下:
button.set({
icon: "images/ok.gif",
caption: "OK",
onClick: function() {
alert("You clicked OK.");
}
});
通过这种赋值方式可以大大的简化代码的书写。 迭代式操作 通过前面的学习我们知道JSON可以构造一个很复杂的树结构的对象,Dorado对象在很多情况下也是树形结构,例如有一个oop为一个Dorado虚拟对象,其中含address虚拟属性,而address本身又是一个Dorado虚拟属性,我们希望取出其中的postCode,如果我们要存取postCode的值,可能的代码有:
oop.get("address").get("postCode");
oop.get("address").set("postCode", "7232-00124");
但是利用虚拟属性的功能,我们可以这么些代码:
oop.get("address.postCode");
oop.set("address.postCode", "7232-00124");
这样看起来代码是不是简洁多了,也很容易的就能读懂代码。对于迭代式get和set方法,还提供了一个更有意义的功能,即加入在迭代过程中遇到的不是一个Dorado的虚拟对象,而是一个标准的JSON对象,则这种迭代操作的处理机制依然有效。
事件
事件 Dorado为虚拟对象增加了很多事件,这些事件与通常Dom的事件并不一样,是Dorado为虚拟对象专门增加的事件,事件的添加方法: 方法一
button1.addListener("onClick", function(self) {
//此方法可以为一个事件添加多个监听函数
});
这是Dorado中内部采用的添加事件的方法。
方法二
button1.set("onClick", function(self) {
// 此方法只能定义一个监听函数
});
这儿要提一下虚拟属性的功能,前面我们介绍过虚拟属性,通过前面的学习上述代码的意图就是设置button1的onClick属性,由于不存在实际的onClick属性,且属性的值我们给了一个function,则Dorado事件引擎会自动的调用button1.addListener处理机制,将这个function注册到事件管理器中。推荐使用第二种方法,代码更为直接,他们之间的差别在于虚拟属性设置方法只能定义一个监听函数,而addListener可以添加多个function。 下面说明事件的几个公用特性,这些特性并不是每一个事件都有,但是这些特性是虚拟对象的事件的基本特性: self和arg Dorado中所有的事件都支持self和arg参数,其中self表示激发事件的自身,如Button激活onClick事件,则self表示Button自身,如Editor激活onClick事件,则self表示Editor对象自身,arg的类型与具体事件类型有关,很多情况下都是一个JSON对象,其中可能包含有非常丰富的传入信息。不同的事件中arg的JSON对象的属性是不一样的,事件A可能arg中含有prop1,prop2,我们可以通过如下代码获取其属性:
var v1 = arg.prop1;
var v2 = arg.prop2;
而事件B中可能传入的信息就是prop3,prop4,则代码就可能为:
var v3 = arg.prop3;
var v4 = arg.prop4;
不同事件的arg的差别,要通过JSDOC查看,例如我们看Button控件: http://dorado.bstek.com/jsdoc/ 其中的onClick事件: 注意其中arg的button,event和returnValue的说明 而其onRefreshDom事件的arg参数就为:
上面两张截图中onClick与onRefreshDom的事件中的arg参数是不一样的。 this 如实做AJAX范例中,按钮Ajax Multiply的onClick事件代码中我们接触到this的写法,但是我们未细讲这个this的含义:
var action = this.get("#multiplyAction");
dorado.MessageBox.prompt("Please input two numbers here", {
defaultText: "3,5",
callback: function(text) {
var nums = text.split(",");
var parameter = {
num1: nums[0],
num2: nums[1]
};
action.set("parameter", parameter).execute(function(result) {
dorado.MessageBox.alert(nums[0] + " * " + nums[1] + " = " + result);
});
}
});
其实这个this指的就是按钮所在的视图View: 几乎所有事件中的this均指向的是该控件隶属的View对象,View对象的onCreate事件中的this是指向其自身的。 onCreate事件中的this是个例外,因为控件总是先被创建然后才被添加到控件树上,因此在onCreate事件被触发时控件并不知道其隶属的View对象。所以onCreate事件中的this并不指向最终的View。而是指自身。
Dorado在所有的事件监听器中提供了一个view隐式变量指向当前事件宿主所属的View,此变量可以完全替代原先this的使用场景。它可以带来以下几个好处: 语义明确,很明显 view.get("#dsPeople") 比 this.get("#dsPeople") 更加准确的表达了代码的含义。不用再担心进入闭包和回调方法之后this的指向发生变化。即使是在控件的onCreate事件中也可以使用view隐式变量。而在之前的版本中,onCreate事件里this的指向是事件宿主自身,与其他事件并不相符。逻辑返回值 所有事件的返回值类型都是逻辑型,用于通知系统是否继续触发同一事件的后续监听器。不返回任何值则系统按true来处理。 一般情况下我们并不需要关注这个逻辑返回值,但有一种特殊情况: Dorado中对象某一个事件可以定义多个Listener的时候,这些Listener会按顺序依次触发,如果我们希望在第一个事件触发的过程中,屏蔽后面Listener的触发,则我们需要在第一个Listener中明确的返回一个false。这样后面的事件就会被跳过,不再执行。 processDefault 很多beforeXXX事件的arg参数中都支持一个名为processDefault的可写属性,用于通知系统是否要执行该事件所代表的后续操作。 如下的一个范例,我们对一个DataType添加一个beforeRemove的事件,并在其中判断,如果是已婚的则不允许删除,通过MessageBox给出提示信息,并设置arg的processDefault为false,用以告诉beforeRemove动作如果是已婚则不执行默认的删除动作。
employeeDataType.addListener("beforeRemove", function(self, arg) {
if (arg.entity.get("married")) {
dorado.MessageBox.alert("已婚的员工不能被删除!");
arg.processDefault = false;
}
});
抛出一个异常,同样可以达到类似的目的。
employeeDataType.addListener("beforeRemove", function(self, arg) {
if (arg.entity.get("married")) {
throw new dorado.Exception("已婚的员工不能被删除!");
}
});
两种方法其实有区别,可能导致不同的结果!例如我们对10条员工记录,循环做删除动作,例如第三条为已婚员工,则如果是用processDefault = false的处理机制,最终系统的效果是删除第三条的时候给出用户警告,但是系统会接着删除第四,五条直到第十条记录,也就是说会继续循环,最终结果是除了第三条记录之外,其它记录全部被删除。但是如果采用throw new dorado.Exception()处理机制,则循环到第三条弹出异常,并终止循环,后面的记录不会不删除。它们之间的差别,用Java关键字表示,就是processDefault相当与循环体中的continue,而throw new Dorado.Exception()相当与break。
更多建议: