初始值重定义
派生类(子类)可能通过继承的方式重定义一个父类中属性定义的初始值。重定义一个属性值只需要在属性的说明中定义两个键:init和refine。refine是一个布尔类型的标志,必须配置为真。
一般情况下属性是不能重写的,这就是使用refine标志的原因,这个标志通知开发者在实现过程中意识到属性的值将被apply修改。
检查传入的值
你可以通过在属性定义中添加相应的check关键字来验证传入的值的正确性。但请你一定记住,这些检查只在应用程序的开发(源)版本中才有效。由于性能优化,我们在构建发布版本时会删除这些检查。如果你确实需要一个属性验证,请查看后面的[验证]部分。
预定义的类型:
• Boolean, String, Number, Integer, Float, Double
• Object, Array, Map
• Class, Mixin, Interface, Theme
• Error, RegExp, Function, Date, Node, Element, Document, Window, Event
由于JavaScript支持数字Number 类型,对于float和double这两种数据类型和Number的处理相同。但qooxdoo加入这两种数据类型也是很有用的因为他们被javadoc文档生成器和API viewer支持。
properties : {
width : { init : 0, check: "Integer" }
}
允许使用的值
可以定义一个列表来标明可以使用的属性值:
properties:{
color:{init:"black",check:["red","blue","orange"]}
}
注:提供一个可能值列表仅适用于原始类型(比如字符串和数字),对引用类型(对象、函数等)不能使用。
实例检查
也可以对一个类的实例进行校验。但这种校验不是类名称检查,而是使用instanceof检查。这也意味着来自于给定的类的实例以及从其继承的类的实例都是合法的。由于类使用字符串定义,所以这不影响类的加载时间。
Properties : {
Logger : {nullable : true, check: "qx.log.Logger" }
}
接口检查
输入的值可以做接口检查,即value(通常是一个类的实例)必须实现给定接口。接口是使用字符串定义,不影响所依赖类的加载时间。
properties : {
application : { check : "qx.application.IApplication" }
}
用户自定义检查的实现
在属性定义内部定义一个函数来检查传入的值是可行的。这也是解决上面列出的内置的检查类型不能解决复杂检查的一个最好用的方法。
properties :{
progress : {
init : 0,
check : function(value) {
return !isNaN(value) && value >= 0 && value <= 100;
}
}
}
这个例子演示了如何限定输入数字值只接受一个给定的数字范围(在0..100。用户自定义检查只受限于开发者的想象力。
做为一种替代方案,你也可以定义一个字符串替代用户自定义检查功能,这个字符串将补充并入setter函数中,因此这是一种非常有效的方式。上面的例子可以重写成这样:
properties : {
progress : {
init : 0,
check : "!isNaN(value) && value >= 0 && value <= 100"
}
}
这种方法效率更高,尤其是涉及相当小的检查测试,因为它不需要在变量上进行函数调用。但是你要确保使用的变量标识符为value,否则就不能检查传入的属性值了。
转换传入的属性值
你可以通过在属性定义中加入transform关键字对传入的属性值被存储之前对它们进行必要的转换。转换方法在check和apply方法调用之前发生,如果传递给它的值无效也可以抛出一个错误。如果你希望接受不同的格式或类型属性值,那么这种方法是有非常有用的。
示例
在这里,我们为width这个属性定义一个check和transform方法。虽然检查方法要求属性值是一个整数,但我们可以使用变换方法接受一个字符串并将其转换为一个整数。请注意,在这里我们仍然可以使用检查方法来捕捉任何不正确的属性值,比如如果用户错误地给属性分配了一个Widget。
properties : {
width : {
init : 0,
transform: "_transformWidth",
check: "Integer"
}
},
members : {
_transformWidth : function(value) {
if( qx.lang.Type.isString(value)){
value = parseInt(value, 10);
}
return value;
}
}
输入值的验证
一个属性的验证可以防止属性值被设置成无效的。如果属性值被设置为无效,验证器应该抛出一个验证错误。否则,验证器可以什么都不做。
使用预定义的验证器
如果你使用了预定义的验证器,当发生错误时他们会为你扔一个验证错误。你可以在qx.util.validate下找到一组预定义的验证器。下面的示例演示了一个范围验证器的使用:
properties : {
application : { validate : qx.util.Validate.range(0, 100) }
}
使用自定义验证器
如果预定义的验证器是不能满足你的验证需求,你也可以指定您自己的验证器:
properties : {
application : {
validate : function(value) {
if (value > 10) {
throw new qx.core.ValidationError("Validation Error: ", value + " is greater than 10.");
}
}
}
}
成员函数做为验证器
你可以在含有属性的类中定义一个验证器方法并其做为类的成员。如果你有这样的成员验证函数,你就可以将方法名称的字符串指定为验证器。
properties : {
application : { validate : "_validateApplication" }
}
使用继承
新的属性系统有另一个很棒的功能就是继承。它主要是针对widgets设计的,但在独立的parent-children结构中它也是可以使用的。
继承是属性系统非常重要的一个功能,在为它它可以大大减少代码冗余,有效的利用的编码和存储空间,因为在一个层次结构中多个对象的值只需要声明一次。子类只要声明一个继承就可以了,只有不需要继承需要重新赋值才需要让子类覆盖这些值。
作为qooxdoo支持的继承,性能可以和CSS的继承相媲美。这意味着,子类中那些没有给出具体定义的属性,它们的值都会自动的使用父类中定义的值。
每个属性也可以有一个显式的字符串“继承”做为用户值。而这个继承的值,通常只作为一个备用的值,因此可以通过设置“继承”来明确强调一下属性的继承性。用户可以为属性设置一个“inherit”来强制属性通过继承查找属性值,它会忽略init和appearance的值。
标记一个属性是可继承的只需要简单添加inheritable设置并将其值设置为true。
properties : {
color : { inheritable : true, nullable : true }
}
或者,您可以将属性的初始值设置为继承。如果属性不能为空这是一个尤其好的主意:
properties : {
color : { inheritable : true, init: "inherit" }
}
继承CSS属性
什么样的用户属性需要使用继承,下面列表中的CSS属性都支持继承,参考一下,可能对你有些帮助:
• color
• cursor
• font, font-family, ...
• line-height
• list-style
• text-align
注意:这个CSS属性清单只是参考,并不能反映任何qooxdoo组件的性能
定义一个属性组
属性组是是一个非常方便的功能,系统可以为属性组自动生成的setters和resetters (但没有getters)的一组属性。下面是一个属性组定义的写法:
properties : {
location : { group : ["left","top"]}
}
正如你所看到的,属性组也像普通属性一样配置了一个键值对。从用户的角度来看,属性组的setters和resetters的API操作等同于普通属性的API操作:
obj.setLocation(50,100);
// instead of
// obj.setLeft(50);
// obj.setTop(100);
速记支持
另外,你还可以在调用属性组的setter方法前对传入的数据按一定的规范进行修改。目前,唯一可以使用的规范就是速记,它是qooxdoo对著名的CSS速记的一个模拟。更多的知识请参阅前文的论述:
properties :
{
padding :
{
group : [ "paddingTop", "paddingRight", "paddingBottom", "paddingLeft" ],
mode : "shorthand"
}
}
对于这个属性,可以使用下面的代码对它的值进行设置:
obj.setPadding( 10, 20 );
// instead of
// obj.setPaddingTop(10);
// obj.setPaddingRight(20);
// obj.setPaddingBottom(10);
// obj.setPaddingLeft(20);
}
什么情况下使用属性
由于qooxdoo的属性运行许多高级功能,如验证、事件等,因此它的代码量相对于只支持setter和getter方法的通用属性要大一些,运行速度要慢一些。如果你不需要这些先进的功能,或者你对存储变量所需的时间非常敏感,这种情况下最好不使用qooxdoo的动态属性功能。你可以在你的对象内声明一个私有变量(例如__varname),然后创建自己的setter和getter(如果需要)访求对变量进行访问和存储。
2.3 应用环境设置
一个应用程序的环境就是一组可以通过定义友好的接口进行查询的值。这些值是通过唯一的键进行引用。你可以认为它们是为应用程序存储的全局键:值对。其是值只需要写一次,就可以多次读取,但也有一些值是只读的。如果某个键的值可以设置,那么就可以通过多种方式,如通过代码,通过构建配置等进行设置,通常这些设置是在应用程序启动时进行的,程序启动后就不在改变。还有一些值会自动根据系统运行环境自动设置,当程序运行时可以对他们直接查询,如当前浏览器的名称,或服务器允许最大连接数等等,利用这种方式,环境的API也实现了浏览器功能的检测,并且这些值不能被任意地设置。
Qooxdoo预告定义了一些环境的键,这些键的值可以由应用程序重新设置。当然应用程序也可以定义自己环境的键并在程序运行时查询它们的值。
综上所述对于应用程序开发人员,环境实际上代表了一组全局的键值对,反映了正在运行的应用程序的运行环境参数。它同时可以用来检测(如浏览器功能),以及对于那些不能从外界提取的全局性键值进行参数注入(如通过构建配置)。在某种程度上讲,这相当于为系统定义了一个静态的应用程序类。
设计环境配置的目的
• 环境设置解决了围绕JavaScript应用的各种需求:
• 加载自定义类之前,控制框架的初始设置。
• 从外面将值传递给应用程序。
• 触发创建多个构建文件。
• 在平台运行时(浏览器引擎,支持HTML5等)的查询功能
• 创建对特定的目标环境进行了优化的代码,即基于特征的构建。
因为在框架中预先定义了一些环境设置,所以在需要使用它们的值的时候你可以通过应用程序代码直接查询它们的值。下一节将讨论这一点。随后,您将学习如何覆盖它们的默认值和定义自己的环境键值对的设置。
查询环境设置
在一般情况下,有两种不同的环境设置类型,同步和异步。异步设置主要是针对检查本身是异步的功能,比如数据检查的URL支持。在qx.core.Environment类中这两种环境类型分别有两种查询方法。同步有get()和select()方法,异步有getAsync()和selectAsync()方法。
同步
让我们先来看看同步API中访问数据的两种可用方法:
qx.core.Environment.get("myapp.key");
这个get方法可能是最重要的方法之一。它返回给定的键的值在这个例子中就是myapp.key键的值。
qx.core.Environment.select("myapp.key", {
"value1" : resvalue1,
"value2" : resvalue2,
"default" : catchAllValue
}
select方法是根据给定键的值来选择推导出值的方法。它也允许你指定特殊的映射键“default”,如果给定的环境键的当前值不匹配任何被映射的值,系统就会使用这个键。在上面的例子中,resvalue(s)可以是一个函数或任何其他有效的JavaScript表达式。
异步
异步方法是同步的直接映射。
qx.core.Environment.getAsync("myapp.key", function(result) {
// callback
}, context);
由于getAsync方法不会立即返回结果,因此你必须指定一个当环境的对应键的值正确返回后立即执行的回调函数。您的回调函数将做为一个参数被传递getAsync方法,回调函数负责将结果与你的应用程序进行集成。
这一原则也适应于相应的select调用:
qx.core.Environment.selectAsync("myapp.key", {
"value" : function(result) {
// callback value 1
},
"default" : function(result) {
// catch all callback
}
}, context)
异步选择值的类型必须是一个函数,当环境的对应键的值正确返回后立即执行此函数。同样,你也可以提供一个“default”键。和使用回调函数的getAsync所使用的回调函数一样,selectAsync也将获得键值作为参数,传递给回调函数,这种参数的传递是“default”的情况时非常有用。
缓存
肯定会发生在应用程序的生命周期中对一些键值进行多次的查询这种情况,如引擎名称。环境系统已经缓存了每个键值来确保最佳的性能。但在一些特殊的情况下你可能需要对环境的键值进行重新计算。在这种情况下你可以使用下面的例子可以使给定键的缓存失效,以强制在下一个查询中重新计算:
qx.core.Environment.invalidateCacheKey("myapp.key"}
这个例子会清除先前myapp.key的计算值。
代码删除
通常情况下,qx.core.Environment.get()函数调用在运行时执行,并返回环境键的给定值。如果所需要的值只在运行时才能确定,或者系统运行时可以改变,这种情况是没有问题的。但是,如果你在生成器的配置中预先确定了一个环境键的值,那么系统生成器就可以预见查询的结果,它就会自动删除那些在运行中不可能使用的代码。
例如:
function foo(a, b) {
if (qx.core.Environment.get("qx.debug") == true) {
if ( (arguments.length != 2) || (typeof a != "string") ) {
throw new Error("Bad arguments!");
}
}
return 3;
}
当qx.debug为false的时候,代码将减少为下面的这样:
function foo(a, b) {
return 3;
}
在使用select调用时:
qx.core.Environment.select("myapp.key", {
"value1" : resvalue1,
"value2" : resvalue2
}
如果myapp.key键的值为value2,那么代码就会减少为:
resvalue2
预定义的环境键
qooxdoo带有一组预定义的环境设置。您可以将这些分成两个大组。第一组是一套对浏览器的功能测试,如对某些CSS或HTML支持功能的测试。第二组是为qooxdoo框架的一些简单设置,驱动框架的内部工作。预定义的环境键的完整列表,请查阅API文档的qx.core.Environment类。
定义新的环境设置
现在我们要实际设置一个新的或覆盖现有的环境设置操作。环境键的值可以采取以下两种形式之一,一个具体的文本值,或在运行时返回一个值函数。前者可以通过各种方式实现(见下文),后者只能通过应用程序代码。(环境键及与它相对应的值也被称为环境设置)。现在,我们学习这两种方式。
文本值
正如已经提到的,可以使用几种不同的方式组给环境键设置文本值(如"foo", 3或true) 。可以使用的定义如下:
• 在类中使用键值对的方式
• 在应用程序代码
• 在index.html文件的<script>脚本中
• 在系统生成器的设置文件中
• 通过URL参数
这份列表的优先级是按升序排列的,也就是说如果一个键被定义多次,系统将提取最高的优先级的设置。下面的章节详细说明这些可以使用的定义方式。
在类中使用键值对的方式,您可以在qooxdoo类定义中通过加入environment关键字来添加键值对的形式定义环境设置:
qx.Class.define("myapp.ClassA",
{
[...]
environment : {
"myapp.key" : value
}
});
在应用程序代码,您可以在类方法使用qx.core.Environment.add来定义一个键值对做为环境设置:
qx.core.Environment.add("key", "value");
在加载的index.html文件的<script>脚本中,在加载你的qooxdoo应用程序的网页以及加载qooxdoo初始文件的<script>标签前,添加另一个<script>标签,并使用window.qx.$$environment分配键值对做为你的环境设置。
<script>
window.qx =
{
$$environment : {
"myapp.key" : value
}
}
</script>
在系统生成器配置的文件中,你可以在jobs(建立你的应用程序的脚本节(如 source-script, build-script))节加入一个environment键,并将做为环境配置的键值对添加到这个键下:
"myjob" :
{
[...]
"environment" : {
"myapp.key" : value
}
}
采用生成器设置环境配置具有特殊的意义,在生成器中对环境键指定一个值的列表可以创建多个输出文件。例如,在上面的例子中用[value1,value2]替换value,生成器将创建两个输出文件。
通过URL参数,使用URL参数来定义环境设置之前,您必须指定另一个环境中,被命名为发电机配置设置qx.allowUrlSettings。如果应用程序是在这个地方配置设置生成的,然后你可以使用URL参数来进一步增加键:值对。
http://my.server.com/path/to/app/index.html?qxenv:myapp.key:value
在URL参数中的图案是容易的。它有由冒号分隔的三个部分。第一部分是固定的qxenv,第二部分是环境设置中的键,最后的部分是设定的值。
注意:qx.allowUrlSettings和“variants”的优化
在生成器配置文件设置qx.allowUrlSettings为true与上面所使用根据变量在系统构建时进行优化有一些矛盾。根据变量在系统构建时优化,可以删除类似调用qx.core.Environment.get()这样的代码,并将它与相应的值替换,而所使用的变量是在生成器的配置文件中定义的。这意味着,如何通过改变URL参数这样来设置环境配置对于上面的优化就没有任何效果,因为调用环境查询的值不再在代码中也没有通过生成器配置文件进行定义,而是通过URL参数进行设置的。
或者,您可以在构建时禁用优化编译,也可以除通过URL参数配置环境的设置。在后一种情况下,你可以用其他的方法来设置默认的配置,如通过中类中加入environment键,或使用类的defer功能调用qx.core.Environment.add()进行配置。
如果在配置文件中设置qx.allowUrlSettings为true,并且启用特定生成的优化,生成器将抛出警告信息。
在前面我们提到,在应用程序代码中使用qx.core.Environment.add来定义一个键值对做为环境设置,其中的值可以用一个函数来取代,此时环境设置在运行时确定。函数(比如特殊CSS或HTML功能检查)一旦执行,就负责返回一个适当的值,供以后根据环境配置的键进行查询。这些检查可以是同步或异步的,与此相对应,也有同步检查函数get()和select()以及异步检查函数getAsync()和selectAsync()。
同步,要添加一个同步检查,标准用法就是使用add():
qx.core.Environment.add("group.feature", function() {
return !!window.feature;
});
这个例子和以前添加键值对做为环境配置的方法相同。唯一的区别是使用函数作为第二个参数,而不是一个简单的值。这个函数负责确定和返回一个值做为给定的环境键的值。在这个例子中,如果window.feature被声明,check将返回真。
异步,要添加一个异步检查,使用addAsync()。 :
qx.core.Environment.addAsync("group.feature", function(callback) {
window.setTimeout(function() {
callback.call(null, true);
}, 1000);
});
这个例子显示了如何添加一个异步功能检查。这个简单的例子中使用超时功能来获取值,当然可以使用更复杂的过程,但超时对于展示这个API操作已经足够了。正如你所看到的在添加检查功能时,它有一个通过getAsync()或selectAsync()异步查询进行调用的称为回调的参数。和之前一样,检查功能负责计算给定的环境的键的值。但是它不返回值(如同步用例那样),而是调用回调函数并传递值。然后回调函数负责和查询结果值集成。在这个简单的示例中,检查函数等待一秒后调用回调函数并返回一个true。