함수 객체

함수 객체 (Function Objects)

AutoHotkey에서 함수 객체(function object)가 무엇인지, 그리고 사용자 정의 펑터(functor)와 BoundFunc 객체를 만드는 방법을 설명하는 문서예요.

출처: 문서

본문

"함수 객체(function object)"는 보통 다음 중 하나를 의미해요:

  • 실제 함수를 나타내는 Func 또는 Closure 객체. 내장 함수이거나 스크립트가 정의한 함수예요.
  • 함수처럼 호출할 수 있는 사용자 정의 객체. 이것을 때로 "펑터(functor)"라고도 불러요.
  • BoundFunc 객체나 COM 메서드가 반환한 JavaScript 함수 객체처럼, 함수처럼 호출할 수 있는 그 밖의 객체.

함수 객체는 다음 용도로 사용될 수 있어요:

  • CallbackCreate
  • GUI: OnCommand
  • GUI: OnEvent
  • GUI: OnNotify
  • For ... in
  • HotIf
  • Hotkey
  • Hotstring
  • InputHook (OnEnd, OnChar, OnKeyDown, OnKeyUp)
  • Menu.Add
  • OnClipboardChange
  • OnError
  • OnExit
  • OnMessage
  • RegEx 콜아웃 (callouts)
  • SetTimer
  • Sort

객체가 호출 가능해 보이는지 알아내려면 다음 중 하나를 사용해요:

  • Value.HasMethod()는 기본적으로 모든 AutoHotkey 값과 객체에서 동작하지만, 일부 객체나 클래스에 대해 HasMethod를 덮어쓸 수 있게 해줘요. COM 객체의 경우, 그 COM 객체가 실제로 다른 프로세스의 AutoHotkey 객체가 아니라면 보통 실패해요(예외를 던지거나 잘못된 결과를 냄).
  • HasMethod(Value)는 모든 AutoHotkey 값과 객체에서 동작하며 덮어쓸 수 없지만, Call 메서드의 존재를 판별할 수 없으면 false를 반환해요. Value가 ComObject이면 예외가 던져져요.

사용자 정의 (User-Defined)

사용자 정의 함수 객체는 "함수"의 구현을 담은 Call 메서드를 반드시 정의해야 해요.

class YourClassName {
    Call(a, b) {  *; 필요에 따라 매개변수를 선언하거나, 배열*.*
        *;...*
        return c
    }
    *;...*
}

이것은 YourClassName()이 반환하는 객체 같은 YourClassName의 *인스턴스(instances)*에 적용돼요. Callstatic Call로 바꾸면 YourClassName 자체가 호출될 때 일어나는 일을 대신 덮어쓰게 돼요.

함수 객체가 프로퍼티에 할당될 때, 메서드 호출 구문(target.func())이 대상 객체를 첫 번째 매개변수로 암시적으로 전달한다는 점을 명심하세요. 위 YourClassName의 경우 this는 함수 객체가 되고 a는 대상 객체가 돼요. 함수 객체를 가져오는 표현식을 괄호로 감싸면 이것을 막을 수 있는데, 아래 예시에 나와 있어요.

예시 (Examples)

다음 예시는 호출 가능한 함수 배열을 정의하며, 호출되면 배열의 각 요소를 차례로 호출해요.

class FuncArrayType extends Array {
    Call(params*) {
        *; 함수 목록을 호출.*
        for fn in this
            fn(params*)
    }
}

*; 함수 배열을 만듦.*
funcArray := FuncArrayType()
*; 배열에 함수를 몇 개 추가함 (어느 시점에든 할 수 있음).*
funcArray.Push(One)
funcArray.Push(Two)
*; 배열을 메서드로 사용하는 객체를 만듦.*
obj := {method: funcArray}
*; 메서드를 호출 (결과적으로 One과 Two를 둘 다 호출).*
obj.method("2nd")
*; 함수로 호출.*
(obj.method)("1st", "2nd")

One(param1, param2) {
    ListVars
    MsgBox
}
Two(param1, param2) {
    ListVars
    MsgBox
}

BoundFunc 객체 (BoundFunc Object)

함수처럼 동작하지만, 미리 정의된 매개변수를 다른 함수에 그냥 전달해요.

BoundFunc 객체를 만드는 방법은 두 가지가 있어요:

  • Func.Bind 메서드를 호출하는 방법. 함수에 매개변수 값을 바인딩해요.
  • ObjBindMethod 함수를 호출하는 방법. 대상 객체에 매개변수 값과 메서드 이름을 바인딩해요.

BoundFunc 객체는 아래 예시처럼 호출될 수 있어요. BoundFunc가 호출되면, 바인딩된 매개변수와 호출자의 매개변수의 조합을 전달하면서 바인딩된 함수나 메서드를 호출해요. 바인딩되지 않은 매개변수 위치에는 호출자의 매개변수 목록에서 왼쪽에서 오른쪽으로 위치가 할당돼요. 예를 들어:

fn := RealFn.Bind(1)  *; 첫 번째 매개변수만 바인딩*
fn(2)      *; "1, 2"를 표시*
fn.Call(3) *; "1, 3"을 표시*

fn := RealFn.Bind( , 1)  *; 두 번째 매개변수만 바인딩*
fn(2, 0)   *; "2, 1, 0"을 표시*
fn.Call(3) *; "3, 1"을 표시*
fn(, 4)    *; 오류: 'a'가 생략됨*

RealFn(a, b, c?) {
    MsgBox a ", " b (IsSet(c) ? ", " c : "")
}

ObjBindMethod는 메서드 자체에 대한 참조를 가져오는 것이 불가능한 경우에도 메서드에 바인딩하는 데 사용될 수 있어요. 예를 들어:

Shell := ComObject("Shell.Application")
RunBox := ObjBindMethod(Shell, "FileRun")
*; Run 대화 상자를 표시.*
RunBox

더 복잡한 예시는 SetTimer를 참조해요.

다른 프로퍼티와 메서드는 Func에서 상속되지만, 대상 함수나 메서드의 프로퍼티를 반영하지는 않아요(그것이 함수로 구현될 필요는 없어요). BoundFunc는 다른 형식 매개변수가 없는 익명 가변 함수(anonymous variadic function)처럼 동작하며, 아래의 굵은 화살표(fat arrow) 함수와 비슷해요:

Func_Bind(fn, bound_args*) {
    return (args*) => (args.InsertAt(1, bound_args*), fn(args*))
}

더 알아보기 (Learn more)