Pre-requisites: AutoHotkey_L and some experience with objects.
A user recently asked for a way to add methods to all objects that are created with [], {}, Object() or Array(). This currently isn't fully supported, but is possible with the following caveats:
- It may stop working in a future version (but not until a better alternative is provided).
- It doesn't apply to "parameter arrays" created by calling a variadic function.
- Additional lines will show in ListLines each time you create an object if ListLines is enabled.
- User-defined classes will not automatically inherit behaviour defined by Object(). However, they can explicitly inherit behaviour via the extends keyword (see example below).
To achieve it, we take advantage of:
- the ability to override any built-in function with a user-defined one.
- the undocumented fact that defining a function named Array overrides
[]
and Object overrides{}
.
However, once a built-in function is overridden, there's no way to call the original function. This is where two of the caveats mentioned above become useful. There are at least two methods to create an object without invoking Object() or Array():
- Define and call a variadic function. The final parameter receives an object (an array of parameters).
- Define a class and use the
new
keyword to create an object derived from it.
To demonstrate, I will utilize both methods to implement commonly desired features:
- Add
Array.length()
, identical to MaxIndex except that it returns 0 instead of an empty string if there are no items. - Prevent
Object.Remove()
from decrementing keys when an integer key is removed.
; Define the base class. class _Array { length() { return Round(this.MaxIndex()) ; Round() turns "" into 0. } } ; Redefine Array(). Array(prm*) { ; Since prm is already an array of the parameters, just give it a ; new base object and return it. Using this method, _Array.__New() ; is not called and any instance variables are not initialized. prm.base := _Array return prm } ; Demonstrate. arr := ["a", "b"] MsgBox % arr.length()
; Define the base class. class _Object { Remove(prm*) { if prm.MaxIndex() = 1 { k := prm[1] if k is integer return ObjRemove(k, "") } return ObjRemove(prm*) } } ; Redefine Object(). Object(prm*) { ; Create a new object derived from _Object. obj := new _Object ; For each pair of parameters, store a key-value pair. Loop % prm.MaxIndex()//2 obj[prm[A_Index*2-1]] := prm[A_Index*2] ; Return the new object. return obj } ; Demonstrate. x := {a: "b", 1: "one", 2: "two"} MsgBox % x.a MsgBox % x[2] x.Remove(1) ; Without the override, x[1] is "two" and x[2] doesn't exist. ; With the override, x[1] is gone and x[2] is still "two". MsgBox % x[2]
; Define a custom class inheriting standard properties from _Object. class MyClass extends _Object { ;... }
Note that _Object.Remove()
will still affect integer keys if a range is removed. For example, obj.Remove(1, 10)
will remove items 1 to 10 and cause obj[11]
to become obj[1]
. Fixing this is left as an exercise for the reader.