AHK的对象和类学习心得

;----------------------------------
; AHK的对象和类学习心得  By FeiYue
;----------------------------------

一、简单对象的使用(把对象当作筐,各种数据往里装,用于读写值)

  AHK-V1:
  简单数组 arr:=[111, 222]
  关联数组 arr:={x:111, y:222, id:"abc"}
  这两种可以用 arr[1] 或 arr.x 中括号或点来混合读写

  AHK-V2:
  简单数组 arr:=[111, 222]
  关联数组 arr:=Map("x",111, "y",222, "id","abc")
  原始对象 arr:={x:111, y:222, id:"abc"}
  前两种应该用 arr[1]、arr["x"] 中括号来读写
  第三种应该用 arr.x 点来读写,不能混用

二、类的使用(复杂对象,封装数据和方法,可以继承类,可以多态重写方法)

  AHK的继承机制类似Lua和JS,是基于原型的,原型是一个普通的父对象。
  原型继承的原理很简单,就是找不到就找原型对象,再找不到就再找原型对象。

  ****************************************

  Lua的继承原理:

  * 继承链:
  ——>子表中找不到属性或方法
  ——>设置元表:setmetatable(子表, 元表对象={__index=父表}),
     元表对象是过渡对象,可以使用下面的技巧省去过渡对象,
     父表.__index=父表, setmetatable(子表, 父表)
  ——>在元表对象的__index属性指向的父表中找,如果找不到
  ——>设置元表:setmetatable(父表, 元表对象={__index=祖父表})
  如果把父表看做类,那么继承的是类的本身对象。

  ****************************************

  AHK-V1的继承原理(类似Lua):

  * 继承链:
  ——>子对象中找不到属性或方法
  ——>设置基对象:(一般通过new自动设置)子对象.base:=父对象
  ——>再在父对象中找,先考虑其中的元函数__Get没有明确return,如果找不到
  ——>设置基对象:父对象.base:=祖父对象
  如果把父对象看做类,那么继承的是类的本身对象。

  * new的处理:子对象:=new 父对象()  (这里的父对象一般为类)
  ——>设置基对象:子对象.base:=父对象
  ——>父对象的非静态变量赋值给子对象,并运行__new()方法初始化子对象
  父对象的静态变量可以被父对象本身使用,非静态变量不能。静态变量可以被实例继承。

  * extends的处理:class 子类 extends 父类
  ——>子类.base:=父类

; AHK-V1 定义类的形式
class ClassName extends BaseClassName
{
    InstanceVar := 表达式        ; 实例变量(实例属性)
    static ClassVar := 表达式    ; 静态变量(类属性)
    class NestedClass           ; 嵌套类
    {
    }
    Method()                ; 方法, 类定义中的函数称作方法
    {
    }
    Property[]              ; 属性定义, 方括号是可选的
    {
        get {
            return ...
        }
        set {
            return ... := value        ; value 在此处为关键字, 不可用其他名称
        }
    }

    ; 下面是元函数,属于语法糖
    __Get([Key, Key2, ...])
    {
    }
    __Set([Key, Key2, ...], Value)
    {
    }
    __Call(Name [, Params...])
    {
    }
    __New(args*)
    {
    }
    __Delete()
    {
    }
    __init()   ; new 时自动生成的,把 this.InstanceVar:=表达式 放入其中
    {
    }
}

对于这个 Class 语法理解如下:
ClassName:={}  ; 类对象本身,也作为本类的原型对象用于下级继承
ClassName.__Class:="ClassName"   ; 自动添加了__Class属性
ClassName.NestedClass:={......}   ; 嵌套类,可以被实例对象看做属性访问
ClassName.ClassVar := Expression   ; 静态变量
ClassName.Method := function (this){}   ; 类中的方法相当于属性值为函数对象
; 动态属性类似元函数,获取值调用get,设置值调用set,相当于属性值为元函数表
ClassName.Property := { get:function (this){}, set:function (this){} }
; 处理未知属性的元函数,获取值用__Get,设置值用__Set,运行方法用__Call
ClassName.__Get := function (this){}
ClassName.__Set := function (this){}
ClassName.__Call := function (this){}
; 生成实例的元函数,生成过程是新建 a:={},然后 a.base:=ClassName,
; 运行__Init方法设置 this.InstanceVar := Expression,非静态变量都保留在实例中
; 而不保留在类对象中,再运行__New获取输入的参数值
ClassName.__New := function (this){}
; 删除实例的元函数,实例的所有引用都取消后会自动运行__Delete
ClassName.__Delete := function (this){}
ClassName.base:=BaseClassName  ; 继承父类,放在最后避免影响自有属性

  ****************************************

  JS的继承原理:

  * 继承链:
  ——>子对象中找不到属性或方法
  ——>设置原型:(一般通过new自动设置)子对象.__proto__=父对象.prototype
  ——>在父对象.prototype属性对应的对象中找,如果找不到
  ——>设置原型:父对象.prototype.__proto__=祖父对象.prototype
  如果把父对象看做构造函数,那么继承的是构造函数的prototype属性对应的对象。

  * new的处理:子对象=new 父对象()  (这里的父对象一般为构造函数)
  ——>父对象的属性和方法初始化到 父对象.prototype 属性对应的对象中
  ,并运行constructor()方法初始化子对象
  ——>设置原型:子对象.__proto__=父对象.prototype

  * extends的处理:class 子对象 extends 父对象
  ——>设置原型:子对象.Prototype.__proto__=父对象.Prototype

  ****************************************

  AHK-V2的继承原理(类似JS):

  * 继承链:
  ——>子对象中找不到属性或方法
  ——>设置基对象:(一般通过new自动设置)子对象.base:=父对象.Prototype
  ——>再在父对象.Prototype属性对应的对象中找,
     先考虑其中的元函数__Get没有明确return,如果找不到
  ——>设置基对象:父对象.Prototype.base:=祖父对象.Prototype
  如果把父对象看做类,那么继承的是类的Prototype属性对应的对象。

  * new的处理:子对象:=父对象()  (这里的父对象一般为类)
  ——>父对象的非静态属性和非静态方法初始化到 父对象.Prototype 属性
     对应的对象中,并运行__new()方法初始化子对象
  ——>设置基对象:子对象.base:=父对象.Prototype
  父对象的静态属性和静态方法、嵌套类不被子对象继承,由父对象本身使用。

  * extends的处理:class 子类 extends 父类
  ——>设置基对象:子类.base:=父类(用于子类操作父类的静态属性和静态方法)
  ——>设置基对象:子类.Prototype.base:=父类.Prototype(用于实例对象继承)

; AHK-V2 定义类的形式
class ClassName extends BaseClassName
{
    InstanceVar := 表达式         ; 实例变量(实例属性,不写入原型对象)
    static ClassVar := 表达式     ; 静态变量(类属性)
    class NestedClass            ; 内置类,只能被类本身调用
    {
    }
    Method()   ; 非静态方法写入原型对象 ClassName.Prototype
    {
    }
    static Method()   ; 静态方法属于类本身
    {
    }
    Property[Parameters]   ; 仅在有参数时使用方括号,动态属性写入原型对象
    {
        get {
            return 属性的值
        }
        set {
            存储或以其他方式处理 值
        }
    }
    ShortProperty   ; 动态属性使用胖箭头
    {
        get => 计算属性值的表达式
        set => 存储或以其他方式处理 值 的表达式
    }
    ShorterProperty => 计算属性值的表达式   ; 动态属性只用get返回值

    ; 下面是元函数,属于语法糖,都写入原型对象
    __Get(Name, Params)
    {
    }
    __Set(Name, Params, Value)
    {
    }
    __Call(Name, Params)
    {
    }
    __New(args*)
    {
    }
    __Delete()
    {
    }
    __init()   ; new 时自动生成的,把 this.InstanceVar:=表达式 放入其中
    {
    }
}

对于这个 Class 语法理解如下:
ClassName:={}   ; 类对象本身
ClassName.Prototype:={}   ; 本类的原型对象用于下级继承
ClassName.Prototype.__Class:="ClassName"   ; 自动添加了__Class属性
ClassName.NestedClass:={......}    ; 内置类,只能被类本身调用
ClassName.ClassVar := Expression   ; 静态变量
ClassName.DefineProp("Method", {call:Method})   ; 静态方法
ClassName.Prototype.DefineProp("Method", {call:Method})   ; 非静态方法
; 动态属性类似元函数,获取值调用get,设置值调用set,相当于属性值为元函数表
ClassName.Prototype.DefineProp("Property", {get:get, set:set})
; 处理未知属性的元函数,获取值用__Get,设置值用__Set,运行方法用__Call
ClassName.Prototype.DefineProp("__Get", {call:__Get})
ClassName.Prototype.DefineProp("__Set", {call:__Set})
ClassName.Prototype.DefineProp("__Call", {call:__Call})
; 生成实例的元函数,生成过程是新建 a:={},然后 a.base:=ClassName.Prototype,
; 运行__Init方法设置 this.InstanceVar := Expression,非静态变量都保留在实例中
; 而不保留在类对象中,再运行__New获取输入的参数值
ClassName.Prototype.DefineProp("__New", {call:__New})
; 删除实例的元函数,实例的所有引用都取消后会自动运行__Delete
ClassName.Prototype.DefineProp("__Delete", {call:__Delete})
ClassName.base:=BaseClassName,  ; 继承父类,放在最后避免影响自有属性
ClassName.Prototype.base:=BaseClassName.Prototype  ; 本类原型继承父类原型

三、AHK-V2类的一些补充

1、Lua的函数是一等公民,可以动态定义、赋值给变量、作为参数传递、作为函数的返回值
  以及存储在数据结构中。Lua的数据结构只有表,属性和方法都是表项目,可以用For遍历。
  AHK-V1的函数虽然不是一等公民,不能动态定义,但是类中定义的函数可以看做属性
  的值为函数对象存储在表中。AHK对象的数据结构实际上也是表,与Lua类似,其自有的
  属性和方法都是表项目,V1可以用For遍历,V2只能用OwnProps()遍历表的非动态属性。
  AHK-V2的胖箭头、嵌套函数和闭包可以在表达式中动态定义函数了,很接近一等公民了,
  但是胖箭头函数还不能实现循环,所以还不是完善的一等公民。

2、DefineProp很强大,可以定义自有的动态属性、方法、值属性,绕过元函数和动态属性。
  Obj.DefineProp(Key, { get:func, set:func, call:func, value:value })
  对象已有的(可能是自有的或继承的)动态属性和方法,不能通过赋值的方式修改,
  只能用DefineProp修改为新的动态属性、方法、值属性,定义值属性等效于赋值。

3、base是继承来的动态属性,所以手动赋值也不会保存在实例的自有属性中,
  如果使用 Obj.DefineProp("base", {value:1}) 设置了实例的自有base属性
  覆盖了继承的动态属性,那么base属性就无特殊效果了,原继承关系不变。

4、MyObject.Property[Parameters] := Value 属性定义不带参数时V2视为数组,与
  (MyObject.Property)[Parameters] := Value 等效,而V1则视为带参数的属性。

5、中括号[]的语法糖,专门给了__Item属性,要把中括号视为 对象.__Item 属性的项目。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值