Differences
This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
| guides:objects [2025-03-03 19:04] – [Function Objects] Words geek | guides:objects [2025-08-01 15:36] (current) – [Maps versus Objects] Add anchor geek | ||
|---|---|---|---|
| Line 156: | Line 156: | ||
| Let's give an example: | Let's give an example: | ||
| - | > Anecdote from [[user: | + | > Anecdote from [[user: |
| > | > | ||
| > Before AHKv2 was even a twinkle of light in the distance, I used various AHK socket libraries to create a chat bot for the AutoHotkey IRC help chat. This chat bot, among other things, kept a scoreboard to track how helpful people were being in the chat. Whenever someone would type '' | > Before AHKv2 was even a twinkle of light in the distance, I used various AHK socket libraries to create a chat bot for the AutoHotkey IRC help chat. This chat bot, among other things, kept a scoreboard to track how helpful people were being in the chat. Whenever someone would type '' | ||
| Line 326: | Line 326: | ||
| </ | </ | ||
| - | ===== Fundamentals of Class Objects | + | ===== Classes |
| - | ==== Function Objects ==== | + | See: [[guides: |
| - | In AutoHotkey v1, there were several global collections of names that were kept separate. There was a collection of command names, a collection of label names, a collection of function names, and a collection of variable names. AutoHotkey v2 has mostly merged these collections, | ||
| - | |||
| - | Allowing functions to be saved inside variables and passed around like data is known as having [[https:// | ||
| - | |||
| - | <runner ahk2> | ||
| - | MyFunction() { | ||
| - | ; No matter how this function is called, the message box | ||
| - | ; will say "You called MyFunction" | ||
| - | MsgBox "You called " A_ThisFunc | ||
| - | } | ||
| - | |||
| - | MsgBox IsObject(MsgBox) ", " Type(MsgBox) | ||
| - | MsgBox IsObject(MyFunction) ", " Type(MyFunction) | ||
| - | |||
| - | MyVar := MyFunction ; Put MyFunction into a different variable | ||
| - | MyVar() ; Call the function object stored inside MyVar | ||
| - | </ | ||
| - | |||
| - | Function objects come inside global read-only variables by default, but can be passed around just like any other object. As shown above, it's easy to put the function object into a different variable even if the new variable has a different name. Additionally, | ||
| - | |||
| - | <runner ahk2> | ||
| - | MyVar := () => MsgBox(" | ||
| - | MyVar() | ||
| - | |||
| - | ; In AHKv2.1 this is allowed as well: | ||
| - | ;MyVar2 := () { | ||
| - | ; MsgBox "You called '" | ||
| - | ;} | ||
| - | ;MyVar2() | ||
| - | </ | ||
| - | |||
| - | By itself, this syntax is usually seen when defining OnEvent type callbacks. It allows you to skip defining a function that might only be called in one place: | ||
| - | |||
| - | <code autohotkey> | ||
| - | CloseCallback() { | ||
| - | MsgBox "You tried to close the GUI" | ||
| - | } | ||
| - | g := Gui() | ||
| - | g.OnEvent(" | ||
| - | |||
| - | ; Can be rewritten as: | ||
| - | |||
| - | g := Gui() | ||
| - | g.OnEvent(" | ||
| - | |||
| - | ; Or in v2.1: | ||
| - | |||
| - | g := Gui() | ||
| - | g.OnEvent(" | ||
| - | MsgBox "You tried to close the GUI" | ||
| - | }) | ||
| - | </ | ||
| - | |||
| - | However, where things start to get really interesting is when you put function objects into //other objects//. Just like a function object can be stored inside a regular variable and then that variable becomes callable, a function object can be stored as an object property and then that property becomes callable. A callable property on an object is called a //method//. | ||
| - | |||
| - | When you call a function stored as an object property, AutoHotkey does a little trick with the parameter list. If you have '' | ||
| - | |||
| - | <runner ahk2> | ||
| - | MyFunction(this, | ||
| - | MsgBox 'a: ' a '`nb: ' b '`nc: ' c | ||
| - | } | ||
| - | |||
| - | MyObject := { | ||
| - | FunctionProperty: | ||
| - | } | ||
| - | |||
| - | ; These following 4 lines are all equivalent | ||
| - | MyObject.FunctionProperty(1, | ||
| - | Temp := MyObject.FunctionProperty, | ||
| - | (MyObject.FunctionProperty)(MyObject, | ||
| - | MyObject.FunctionProperty.Call(MyObject, | ||
| - | </ | ||
| - | |||
| - | ==== Prototyping ==== | ||
| - | |||
| - | AutoHotkey objects are *prototype* based, but AutoHotkey' | ||
| - | |||
| - | The first part of this arrangement was //function objects// being nested inside regular objects. The second part is // | ||
| - | <runner ahk2> | ||
| - | baseObject := { | ||
| - | someProperty: | ||
| - | } | ||
| - | |||
| - | testObject := { | ||
| - | base: baseObject | ||
| - | } | ||
| - | |||
| - | MsgBox testObject.someProperty ; Will show " | ||
| - | </ | ||
| - | |||
| - | ==== Class Syntax ==== | ||
| - | |||
| - | AutoHotkey' | ||
| - | |||
| - | Class syntax is used to simultaneously define two things: a " | ||
| - | |||
| - | Remembering the fundamental of how functions stored in objects are called, it would mean that in this following example, when '' | ||
| - | |||
| - | <runner ahk2> | ||
| - | testMethod(this, | ||
| - | MsgBox "this Ptr: " ObjPtr(this) | ||
| - | MsgBox 'a: ' a '`nb: ' b '`nc: ' c | ||
| - | } | ||
| - | MyClass := { | ||
| - | Prototype: { | ||
| - | | ||
| - | } | ||
| - | } | ||
| - | |||
| - | myObject := {base: MyClass.Prototype} | ||
| - | |||
| - | MsgBox " | ||
| - | MsgBox " | ||
| - | |||
| - | myObject.functionProperty(1, | ||
| - | </ | ||
| - | |||
| - | The "class object" | ||
| - | |||
| - | An instance factory is a function that creates instances of a class. An instance factory for an AHK class works something like this: | ||
| - | |||
| - | <code autohotkey> | ||
| - | classFactory(someClass) { | ||
| - | instance := {base: someClass.Prototype} | ||
| - | instance.__Init() | ||
| - | if HasMethod(instance, | ||
| - | instance.__New() | ||
| - | } | ||
| - | return instance | ||
| - | } | ||
| - | </ | ||
| - | The instance factory gets put onto the class object as its " | ||
| - | <runner ahk2> | ||
| - | testMethod(this, | ||
| - | MsgBox 'a: ' a '`nb: ' b '`nc: ' c | ||
| - | } | ||
| - | classFactory(someClass) { | ||
| - | instance := {base: someClass.Prototype} | ||
| - | instance.__Init() | ||
| - | if HasMethod(instance, | ||
| - | instance.__New() | ||
| - | } | ||
| - | return instance | ||
| - | } | ||
| - | MyClass := { | ||
| - | Prototype: { | ||
| - | | ||
| - | }, | ||
| - | Call: classFactory | ||
| - | } | ||
| - | myInstance := MyClass() | ||
| - | myInstance.functionProperty(" | ||
| - | </ | ||
| - | |||
| - | This code invokes " | ||
| - | |||
| - | That's the vast majority of what class syntax does. In that last example, we manually created this class: | ||
| - | |||
| - | <runner ahk2> | ||
| - | class MyClass { | ||
| - | functionProperty(a, | ||
| - | MsgBox 'a: ' a '`nb: ' b '`nc: ' c | ||
| - | } | ||
| - | } | ||
| - | myInstance := MyClass() | ||
| - | myInstance.functionProperty(" | ||
| - | </ | ||
| - | |||
| - | When defining a class, it allows you to specify static and non-static properties. You can do exactly the same with the manually written code. Static properties get added to the class object. Non-static properties get added to the instance by the '' | ||
| - | |||
| - | <runner ahk2> | ||
| - | class MyClass1 { | ||
| - | static someProp := 123 | ||
| - | someProp := 456 | ||
| - | } | ||
| - | myInstance1 := MyClass1() | ||
| - | MsgBox " | ||
| - | |||
| - | ; Equivalent to | ||
| - | |||
| - | classFactory(someClass) { | ||
| - | instance := {base: someClass.Prototype} | ||
| - | instance.__Init() | ||
| - | if HasMethod(instance, | ||
| - | instance.__New() | ||
| - | } | ||
| - | return instance | ||
| - | } | ||
| - | MyClass2 := { | ||
| - | Prototype: { | ||
| - | | ||
| - | }, | ||
| - | Call: classFactory, | ||
| - | someProp: 123 | ||
| - | } | ||
| - | myInstance2 := MyClass2() | ||
| - | MsgBox " | ||
| - | </ | ||
| - | |||
| - | === Unique behavior === | ||
| - | |||
| - | As mentioned previously, there are a few unique features of the '' | ||
| - | |||
| - | The first is // | ||
| - | |||
| - | The second difference is that the variable defined using '' | ||