aardio 文档

aardio 常见陷阱

每一个编程语言都无法避免地存在各种各样的陷阱, 如果我们事先了解它们就可以善用这些语法特性并且避免问题。

▶ 多返回值与不定参数 #

在函数实参尾部调用有多个返回值的函数时,会增加多个调用参数。

示例:

import console;

var tab = {}

table.push( tab,tonumber("1") );
table.push( tab,tonumber("123") );

console.log("你觉得tab应当有两个成员吗,下面我们看看 tab 里面是什么:")

// tab 里面实际有四个元素,超出了我们的预期,这是怎么回事?
console.varDump( tab )

aardio 支持多个返回值,也支持不定参数, 当这些好用的功能在你没有准备的时候碰到一起,就制造了一个可能的陷阱。

tonumber() 转换字符串为数值,成功则返回两个值,一个是转换后的数值, 一个是该数值在字符串中的字节长度。例如 tonumber("123") 返回 123,3

而 table.push() 函数接受不定个数的参数,它会将所有参数添加到数组的尾部。

看: table.push( tab,tonumber("123") );
在执行时就变成了这样: table.push( tab,123,3 );

那么如何解决这个问题呢?
很简单, 向下面这样加一对圆括号就可以:

table.push( tab,( tonumber("123") ) );

圆括号建立的单值表达式只会返回一个值。

参考:table.push

return 语句后面被忽略的代码 #

在 aardio 中, return 语句后面的代码会被解释为注释语句。

例如:

import console; 

console.log(
    function(){
        return (123) )))))
    }
)
console.pause();

上面的代码看起来似乎存在语句错误,但实际上是正确的。虽然这不会出什么问题,但应当避免这样写代码。

参考:return 语句

▶ 数组 #

aardio 数组的起始索引为 1 而不是 0,这一点要特别注意。

aardio 数组非常灵活,可以包含任意类型的数据,也可以嵌套,甚至可以包含非数组成员。

在 aardio 中哈希表与数组都是表( table ), 表可以包含这两种类型的成员。

基本规则:

aardio 中的数组操作函数,取数组长度的 # 操作符都只能用于有序数组。如果有序数组中包含 null 值,仍然作为有序数组操作则 aardio 并不保证数组的有效范围包含或不包含 null 值。

正确的方法是避免将类似 {1,null,null,null,null,5,6} 这样的稀疏数组作为有序数组进行操作,例如应当使用 table.range 函数获取稀疏数组的索引范围而不是使用 # 操作符去获取有序数组长度。

参考:

▶ 字符串连接与数值加运算 #

数值加运算符为 +
字符串连接操作符为 ++

算术加与字符串连接是完全不同的操作,应当明确区分。

+ 运算符的两个操作数都是字符串,且任一操作数无法转换为数值时则转换为字符串连接。不应依赖此规则做字符串连接,以避免误操作与不必要的转换。

引号(双引号、单引号、反引号)前后的 + 运算符将自动转换为字符串连接运算符 ++。例如 str + "" 等价于 str ++ "" 。这种写法不会有歧义,在 aardio 中也很常见。

但不能因为经常看到 str + "" 这样的代码,就认为使用 str1 + str2 适合用于连接字符串。

实际上,如果 str1 + str2 的两个操作符都是字符串,且任一字符串不能转换为数值,就会转换为字符串连接。这会让我们更容易误以为 str1 + str2 的写法是正确的,直到有一天其中一个操作数可以转换为数值,bug 就来了。

举例:

var str = ""

var str2 = 2;


/*
两个字符串相加,如果其中一个字符串可以转换为数值,
则执行算术加运算而非连接操作。

因为 str 之前存储的空字符串能自动转换为数值 0,
所以下面的 str 在与 str2 自动做算术加运算以后变成了数值 2。
*/
str += str2;

var str3 = "abc"

//报错,字符串 "abc" 不能与数值相加。
str += str3;

参考:自动类型转换 + 运算符

正确写法是明确地用 ++ 连接字符串:

var str = "";
var str2 = 2;
var str3 = "abc";

str ++= str2;
str ++= str3

参考: ++ 操作符

▶ 等式判断与逻辑值转换 #

aardio 的等式判断与逻辑值转换规则是比较容易掌握的。

最主要的规则:

1. 逻辑值比较

根据前述原则:

false == 0 的结果为 true 。
true == ""的结果也为 true 。

因为以上等式中有一个操作数是布尔值,所以全部操作数都会转换为逻辑值以后再比较。

2. 数值比较

当我们将 0 == "" 比较时结果也是 true。

这是因为当操作数中没有布尔值且存在数值时,会转换为数值进行比较。"" 转换为数值则为 0,所以与 0 是相等的。

当等式或不等式的操作数中只有数值而没有出现布尔值,就不应当错误地根据布尔值转换规则去推导结果。

try catch 只能用 return 语句退出自身 #

参考:try catch

在 aardio 中 try catch 的行为更像一个立即执行的匿名函数。

try catch 内部的 return 语句只会中断并退出 try catch 自身,而不会退出包含 try catch 的函数。

而且也只能用 return 中断并退出 try catch 语句,不能对包含 try catch 的外部循环使用 breakcontinue 语句。

如果需要在退出 try catch 语句以后退出函数, 那么需要在 try catch 语句中使用一个额外的错误标记,然后在 try catch 语句结束后检查该标记并退出函数。

示例:

var func = function(){

    var err;
    try {
        error("出错了")
        return 123;
    }
    catch( e ){
        err = e;
    }

    if( err ){
        return null,err;
    }

    return 456;
}

这的确带来一点小麻烦,但是如果擅于运用这个特性也会带来好处。

例如:

try {

    if( 条件1 ){
        return;
    }

    if( 条件2 ){
        return;
    }

    if( 条件3 ){
        return;
    }  

}  

任何特性用不好就是陷阱,用好了也就不是陷阱了。

▶ 隐蔽的 _WM_QUIT #

在窗口程序中,win.loopMessage() 负责启动消息循环。

消息循环实际上是一个 while 循环,它就象公司的前台,不断的接待各种用户操作请求,然后分发给窗体回调函数。

参考:win.loopMessage

在 aardio 中开发窗口程序非常简单,我们不需要显式地指定应用程序主窗体。当所有窗口退出,会自动退出 win.quitMessage() , 所有事会自动地帮你处理得妥妥当当。

但是这又导致了一个新的问题。

我们看下面的代码。

import win.ui;
/*DSG{{*/
var winform = ..win.form( scroll=1;bottom=399;parent=...;text="aardio Form";right=599 )
winform.add(  )
/*}}*/

winform.show(); //显示窗体
winform.close() //关闭窗体

/*
所有窗体退出了, 看起来消息循环也不需要了?
win.loopMessage() 看来也可以省略掉了!?
*/

//下面我们制造一个程序异常,理论上我们应当看到IDE弹出错误信息
error("出错")

怎么回事? 我们并没有看到错误信息 ! 也就是说这后面的代码要是出错了,我们将看不到任何提示。

呃,窗口程序的机制可不是想象的那么简单。 win.loopMessage() 还做了很多其他的事,例如在所有窗口退出以后,负责打扫卫生 - 清除所有的 _WM_QUIT 退出消息。

解决办法是加上 win.loopMessage()。如果你忘记了这件事,那这些到处乱跑的 _WM_QUIT 会“帮助”你关掉你的错误对话框。

正确代码如下:

import win.ui;
/*DSG{{*/
var winform = ..win.form( scroll=1;bottom=399;parent=...;text="aardio Form";right=599 )
winform.add(  )
/*}}*/

winform.show();
winform.close();

//窗口程序不能缺少下面这句
win.loopMessage();

error("出错");

Markdown 格式