目录
- 入门和简单示例
- 参数
- 可选参数
- 返回值给调用者
- 可变参数函数
- 局部变量
- 动态调用函数
- 优化布尔求值
- 在函数中使用子程序
- Return, Exit 及一般说明
- 使用 #Include 在多个脚本间共享函数
- 函数库: 标准库和用户库
- 内置函数
入门和简单示例
函数类似于子程序 (Gosub), 不过它可以从调用者那里接受参数 (输入). 同时, 函数还可以返回值给其调用者. 思考后面这个接受两个数字并返回它们的和的简单函数:
Add(x, y)
{
return x + y ; "Return" 期望 表达式.
}
上面的被称为函数 定义, 因为它创建了一个名称为 "Add" (不区分大小写) 的函数并且确立了调用它时必须准确的提供两个参数 (x 和 y). 要调用此函数, 请把它的结果通过 := 运算符 赋值给变量. 例如:
Var := Add(2, 3) ; 数字 5 将被保存到 Var.
并且, 调用函数时可以不保存其返回值:
Add(2, 3)
但在这种情况下, 函数返回的任何值会被丢弃; 所以除非函数产生了返回值外的其他效果, 否则这个调用没有意义.
由于函数调用是 表达式, 在其参数列表中的任何变量名称都不应该括在百分号中. 与之相比, 原义的字符串应该括在双引号中. 例如:
if InStr(MyVar, "fox") MsgBox The variable MyVar contains the word fox.
最后, 可以在任何命令的参数中调用函数 (除了像 StringLen 的中的那些 OutputVar 和 InputVar 参数). 然而, 不支持 表达式 的参数必须加上 "% " 前缀, 例如:
MsgBox % "The answer is: " . Add(3, 2)
在原生支持表达式的参数中也允许加上 "% " 前缀, 不过它会被简单的忽略了.
参数
定义函数时, 其参数都在其名称后面的括号中列出 (在其名称和开括号之间不能含有空格). 如果函数不接受任何参数,请把括号留空,例如:GetCurrentTimestamp()
.
ByRef 参数: 从函数的角度看, 参数本质上是 局部变量, 除非它们被定义为 ByRef, 例如:
Swap(ByRef Left, ByRef Right) { temp := Left Left := Right Right := temp }
在上述例子中, ByRef 的使用让每个参数变成从调用者传递进来的变量的一个别名. 换句话说, 参数和调用者的变量都引用内存中相同的内容. 这样使得 Swap 函数可以通过移动 Left 的内容到 Right 中来修改调用者的变量, 反之亦然.
与之相比, 在上述例子中如果没有使用 ByRef, Left 和 Right 将是调用者变量的副本, 因此 Swap 函数不会对外部产生影响.
由于 return 只能送回一个值给函数的调用者, 所以可以使用 ByRef 送回更多的结果. 这是由函数向调用者传递进来的变量 (通常为空) 储存一个值来实现的.
传递大字符串给函数时, 使用 ByRef 提高了性能并且通过避免生成字符串的副本节约了内存. 同样地, 使用 ByRef 送回长字符串给调用者通常比类似 Return HugeString
的方式执行的更好.
[AHK_L 60+]: 如果传递给 ByRef 参数的不是可修改的变量, 那么函数会表现的就像关键字 "ByRef" 没有那样. 例如, Swap(A_Index, i)
保存 A_Index 的值到 i 中, 但是当 Swap 函数返回时赋给 Left 的值会被丢弃.
[v1.1.01+]:IsByRef() 函数可以用来判断调用者是否为指定的 ByRef 参数提供了变量.
已知限制:
- 对象字段在 ByRef 的目的中不会被视为变量. 例如, 如果
foo.bar
传递给 ByRef 参数, 那么它将表现的就像 ByRef 没有那样. - 不能传递 Clipboard, 内置变量 或 环境变量 给函数的 ByRef 参数, 即使脚本中没有 #NoEnv 时.
- 尽管函数可以递归调用它自己,但是如果它传递它自己的一个局部变量或非 ByRef 参数给自己的 ByRef,那么新一层的 ByRef 参数将引用它自己那个名称的局部变量而不是之前层的.然而, 当函数传递给它自己 全局变量, 静态变量 或 ByRef 参数时不会产生这样的问题.
- 如果一个参数在函数调用中被解析为一个变量 (例如
Var
或++Var
或Var*=2
), 它左边或右边的其他参数可能在它被传递给函数前修改这个变量. 例如, 当 Var 初始为 0 时func(Var, Var++)
会意外地传递 1 和 0, 即使函数的首个参数不是 ByRef 类型时. 因为这种行为是违反常规的, 所以可能在将来的版本中改变. - ByRef 不直接支持函数调用 COM 客户端, 或调用 COM 方法. 作为替代, 脚本必须接受或传递一个 wrapper object (包装对象) 包含特殊 VarType (变量类型) 和变量内存地址.
可选参数
定义函数时, 可以把它的一个或多个参数标记为可选的. 这可以通过在参数后添加一个等号(在 v1.1.09+ 中还可以为 :=
)跟着默认值完成. 后面的函数中其 Z 参数被标记为可选的:
Add(X, Y, Z:=0) { return X + Y + Z }
从 v1.1.09 起,=
和 :=
都支持.为了与表达式赋值保持一致且与将来的 AutoHotkey 版本兼容,建议使用后者.
当调用者传递 三个 参数给上面的函数时, Z 的默认值被忽略. 但当调用者仅传递 两个 参数时, Z 自动接受默认值 0.
可选参数不能孤立地放在参数列表的中间. 换句话说, 在首个可选参数右边的所有参数都必须标记为可选的. [AHK_L 31+]:调用函数时可以省略参数列表中间的可选参数,如下所示:对于动态的函数调用和方法调用,版本为 v1.1.12+ 才支持这种特性.
Func(1,, 3)
Func(X, Y:=2, Z:=0) { ; 请注意此时 Z 必须是可选的.
MsgBox %X%, %Y%, %Z%
}
在 v1.0.46.13+, ByRef 参数 也支持默认值; 例如: Func(ByRef p1 = "")
. 每当调用者省略这样的参数时, 函数会创建一个包含默认值的局部变量; 换句话说, 函数表现的就像关键字 "ByRef" 没有那样.
参数的默认值必须是下列形式的其中一种: