Dorado 7 : 02. 虚拟面向对象(SEFC)

由于JavaScript本身并不支持面向对象,但是我们可以利用其Prototype去模拟部分面向对象的语言特征。我们这儿要说的虚拟面向对象就是指Dorado7中提供的JavaScript模拟面向对象的编程技术。

在基础教程中我们只介绍Dorado JS对象的使用和相关约定,而忽略对象的声明、扩展等内容。

虚拟属性

我们使用Dorado提供的JSDOC的时候注意到,其中Dorado的JS对象通常都包含很多的attributes,如我们访问URL: http://www.bsdn.org/projects/dorado7/deploy/jsdoc/

访问dorado.widget下的Button控件:

这些attributes就是Dorado7对象的虚拟属性,之所以称它们为虚拟属性是,因为它们与Java中的属性不同的地方在于,Java中的属性我们都可以通过专用的getXXX/setXXX方法进行读取操作,但是Dorado7对象的虚拟属性我们需要通过如下的方式:

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

Dorado7中所有的事件都支持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://bsdn.org/projects/dorado7/deploy/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。而是指自身。

Dorado7.1在所有的事件监听器中提供了一个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。

 

 

 

Attachments:

ButtonAttributes.png (image/png)
ButtonOnClick.png (image/png)
ButtonOnRefreshDom.png (image/png)
AjaxView.png (image/png)