每一个编程语言都无法避免地存在各种各样的陷阱, 如果我们事先了解它们就可以善用这些语法特性并且避免问题。
在函数实参尾部调用有多个返回值的函数时,会增加多个调用参数。
示例:
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 的等式判断与逻辑值转换规则是比较容易掌握的。
最主要的规则:
根据前述原则:
false == 0
的结果为 true 。
true == ""
的结果也为 true 。
因为以上等式中有一个操作数是布尔值,所以全部操作数都会转换为逻辑值以后再比较。
当我们将 0 == ""
比较时结果也是 true。
这是因为当操作数中没有布尔值且存在数值时,会转换为数值进行比较。""
转换为数值则为 0
,所以与 0 是相等的。
当等式或不等式的操作数中只有数值而没有出现布尔值,就不应当错误地根据布尔值转换规则去推导结果。
try catch
只能用 return
语句退出自身 #参考:try catch
在 aardio 中 try catch
的行为更像一个立即执行的匿名函数。
在 try catch
内部的 return
语句只会中断并退出 try catch
自身,而不会退出包含 try catch
的函数。
而且也只能用 return
中断并退出 try catch
语句,不能对包含 try catch
的外部循环使用 break
或 continue
语句。
如果需要在退出 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;
}
}
任何特性用不好就是陷阱,用好了也就不是陷阱了。
在窗口程序中,win.loopMessage()
负责启动消息循环。
消息循环实际上是一个 while
循环,它就象公司的前台,不断的接待各种用户操作请求,然后分发给窗体回调函数。
在 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("出错");