[AHK2] 向对象原型添加属性和方法

发布时间 2023-10-22 18:10:28作者: 落寞的雪

ahk和js十分相似,其中一点就是可以向本地对象添加自定义方法和属性。
下面的脚本向ahk的字符串,数组添加了许多方法,添加之后在使用上就和js更加相似了。

; This script is used to extend the methods of the ahk native object prototype

#Requires AutoHotkey v2.0
#SingleInstance Force

Undefined := ''

noop(*) {
}

; # String
DefProp := {}.DefineProp
DefProp("".base, "At", { call: _At_String })
DefProp("".base, "CharAt", { call: _CharAt })
DefProp("".base, "CharCodeAt", { call: _CharCodeAt })
DefProp("".base, "ToCharArray", { call: _ToCharArray })
DefProp("".base, "Concat", { call: _Concat_String })
DefProp("".base, "EndWith", { call: _EndWith })
DefProp("".base, "Repeat", { call: _Repeat })
DefProp("".base, "ToLowerCase", { call: _ToLowerCase })
DefProp("".base, "ToUpperCase", { call: _ToUpperCase })
DefProp("".base, "ToTitleCase", { call: _ToTitleCase })
DefProp("".base, "Length", { get: StrLen })
DefProp("".base, "IsString", { get: (*) => true })

; 返回指定位置的字符,如果是负数从字符串末尾开始。
_At_String(this, index) {
  charArr := this.ToCharArray()
  if index > 0
    return charArr[index]
  else if index < 0
    return charArr[this.Length + index + 1]
  else
    return Undefined
}
; 返回指定位置的字符,不接受负数。
_CharAt(this, index) {
  if index <= 0 || index > this.Length
    return Undefined
  charArr := StrSplit(this)
  return charArr[index]
}
; 返回指定位置的字符码元
_CharCodeAt(this, index) {
  char := this.CharAt(index)
  return Ord(char)
}
; 返回字符串的字符数组、
_ToCharArray(this) {
  return StrSplit(this)
}
; 将字符串参数连接到调用的字符串,并返回一个新的字符串。
_Concat_String(this, str*) {
  r := this
  for v in str {
    r .= v
  }
  return r
}
; 判断一个字符串是否以指定字符串结尾,如果是则返回 true,否则返回 false。
; 第二个参数指定预期结尾位置,如果字符串在该位置结束,返回 true,否则返回 false。
_EndWith(this, searchString, endPostion?) {
  sl := searchString.Length
  if IsSet(endPostion) {
    if sl < endPostion {
      target := SubStr(this, endPostion - sl + 1, sl)
      if target = searchString
        return true
    }
    return false
  } else {
    return searchString = SubStr(this, this.Length - sl + 1)
  }
}
; 构造并返回一个新字符串,其中包含指定数量的所调用的字符串副本,这些副本连接在一起。
_Repeat(this, count) {
  if count < 0
    throw Error('RangeError')
  count := Floor(count)
  loop count {
    r .= this
  }
  return r
}
_ToLowerCase(this) {
  return StrLower(this)
}
_ToUpperCase(this) {
  return StrUpper(this)
}
_ToTitleCase(this) {
  return StrTitle(this)
}

; # Array
arrProto := Array.Prototype
arrProto.DefineProp("Concat", { call: _Concat_Array })
arrProto.DefineProp("Peek", { call: _Peek })
arrProto.DefineProp("At", { call: _At_Array })
arrProto.DefineProp("Every", { call: _Every })
arrProto.DefineProp("Fill", { call: _Fill })
arrProto.DefineProp("Filter", { call: _Filter })
arrProto.DefineProp("Find", { call: _Find })
arrProto.DefineProp("FindIndex", { call: _FindIndex })
arrProto.DefineProp("FindLast", { call: _FindLast })
arrProto.DefineProp("FindLastIndex", { call: _FindLastIndex })
arrProto.DefineProp("ForEach", { call: _ForEach })
arrProto.DefineProp("DeepClone", { call: _DeepClone })
arrProto.DefineProp("Includes", { call: _Includes })
arrProto.DefineProp("Join", { call: _Join })
arrProto.DefineProp("Map", { call: _Map })
arrProto.DefineProp("Reduce", { call: _Reduce })
arrProto.DefineProp("ToString", { call: _ToString })
arrProto.DefineProp("Reverse", { call: _Reverse })
arrProto.DefineProp("ToReverse", { call: _ToReverse })
arrProto.DefineProp("Shift", { call: _Shift })

Array.DefineProp('From', { call: _From })

; 静态方法从字符串或数组创建一个新的**深拷贝**的数组实例。
_From(this, arrayLike, mapFn?) {
  if not (arrayLike is Array or arrayLike.isString)
    throw Error('invalid param')
  if IsSet(mapFn) {
    switch mapFn.MaxParams {
      case 1: fn := (v, *) => mapFn(v)
      case 2: fn := (v, index, *) => mapFn(v, index)
      default: throw Error('invalid callback function')
    }
  } else fn := (v, *) => v
  arr := []
  if arrayLike is Array {
    for v in arrayLike {
      arr.Push(fn(v, A_Index))
    }
  } else {
    arr := arrayLike.ToCharArray()
  }
  return arr
}
; 将两个或多个数组或值合并成一个新数组。
; 此方法不会更改现有数组,而是返回一个新数组。
_Concat_Array(this, value*) {
  r := this.DeepClone()
  for v in value {
    if value is Integer {
      r.Push(v)
    } else {
      for vv in v {
        r.push(vv)
      }
    }
  }
  return r
}
; 查看数组末尾元素
_Peek(this) {
  return this[this.Length]
}
; 方法接收一个整数值并返回该索引对应的元素,允许正数和负数。
; 负整数从数组中的最后一个元素开始倒数。
_At_Array(this, index) {
  return index > 0 ? this[index] : this[this.Length + index]
}
; 测试一个数组内的所有元素是否都能通过指定函数的测试。
; 它返回一个布尔值。
_Every(this, cb) {
  copy := this
  switch cb.MaxParams {
    case 1: fn := (v, *) => cb(v)
    case 2: fn := (v, index, *) => cb(v, index)
    case 3: fn := (v, index, arr) => cb(v, index, arr)
    default: throw Error('invalid callback function')
  }
  for v in copy {
    if !fn(v, A_Index, copy)
      return false
  }
  return true
}
; 用一个固定值填充一个数组中从起始索引(默认为 0)到终止索引(默认为 array.length)内的全部元素。
; 它返回修改后的数组。
_Fill(this, value, start?, end?) {
  l := this.Length
  if IsSet(start) {
    if IsSet(end) {
      end := end > l ? l : end
      start := start > end ? end : start
      d := end - start
    } else {
      d := start > l ? l : l - start
    }
    loop d + 1 {
      this[start + A_Index - 1] := value
    }
  } else {
    loop l {
      this[A_Index] := value
    }
  }
}
; 创建给定数组一部分的**深拷贝**(与js不同),其包含通过所提供函数实现的测试的所有元素。
_Filter(this, cb) {
  r := []
  switch cb.MaxParams {
    case 1: fn := (v, *) => cb(v)
    case 2: fn := (v, index, *) => cb(v, index)
    case 3: fn := (v, index, arr) => cb(v, index, arr)
    default: throw Error('invalid callback function')
  }
  for v in this {
    if fn(v, A_Index, this)
      r.Push(v)
  }
  return r
}
; 返回数组中满足提供的测试函数的第一个元素的值。否则返回 空。
_Find(this, cb) {
  for v in this {
    if cb(v)
      return v
  }
}
; 返回数组中满足提供的测试函数的第一个元素的索引。
; 若没有找到对应元素则返回 -1。
_FindIndex(this, cb) {
  for v in this {
    if cb(v)
      return A_Index
  }
  return -1
}
; 反向迭代数组,并返回满足提供的测试函数的第一个元素的值。
_FindLast(this, cb) {
  loop this.Length {
    if cb(v := this.Pop())
      return v
  }
}
; 反向迭代数组,并返回满足提供的测试函数的第一个元素的索引。
_FindLastIndex(this, cb) {
  l := this.Length
  loop l {
    if cb(this.Pop())
      return l - A_Index + 1
  }
  return -1
}
; 对数组的每个元素执行一次给定的函数。
_ForEach(this, cb) {
  switch cb.MaxParams {
    case 1: fn := (v, *) => cb(v)
    case 2: fn := (v, index, *) => cb(v, index)
    case 3: fn := (v, index, arr) => cb(v, index, arr)
    default: throw Error('invalid callback function')
  }
  for v in this {
    fn(v, A_Index, this)
  }
}
; 返回数组的深拷贝。
_DeepClone(this) {
  arr := []
  for v in this {
    arr.Push(v)
  }
  return arr
}
; 用指定分隔符连接数组元素。
; 返回一个字符串
_Join(this, separator?) {
  sep := IsSet(separator) ? separator : ','
  for v in this {
    if A_Index != this.Length
      r .= v sep
    else
      r .= v
  }
  return r
}
; 用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回 false。
_Includes(this, searchElement, fromIndex?) {
  if IsSet(fromIndex) {
    l := this.Length
    if fromIndex > 0 {
      if fromIndex > l
        return false
      i := fromIndex
    } else {
      if fromIndex < -l || fromIndex = 0
        return this.FindIndex((v) => v = searchElement) != -1
      i := l + fromIndex + 1
    }
    c := l - i + 1
    loop c {
      if this[i + A_Index - 1] = searchElement
        return true
    }
    return false
  } else {
    return this.FindIndex((v) => v = searchElement) != -1
  }
}
; 创建一个新数组,这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成。
_Map(this, cb) {
  switch cb.MaxParams {
    case 1: fn := (v, *) => cb(v)
    case 2: fn := (v, index, *) => cb(v, index)
    case 3: fn := (v, index, arr) => cb(v, index, arr)
    default: throw Error('invalid callback function')
  }
  res := []
  for v in this {
    res.push(fn(v, A_Index, this))
  }
  return res
}
; 对数组中的每个元素按序执行一个提供的 reducer 函数,每一次运行 reducer 会将先前元素的计算结果作为参数传入。
; 最后将其结果汇总为单个返回值。
_Reduce(this, cb, initialValue?) {
  l := this.Length
  if !l and !IsSet(initialValue)
    throw TypeError()
  else if !l and IsSet(initialValue) {
    return initialValue
  } else if l = 1
    return this[1]
  switch cb.MaxParams {
    case 1: fn := (p1, *) => cb(p1)
    case 2: fn := (p1, p2, *) => cb(p1, p2)
    case 3: fn := (p1, p2, p3, *) => cb(p1, p2, p3)
    case 4: fn := (p1, p2, p3, p4) => cb(p1, p2, p3, p4)
    default: throw Error('invalid callback function')
  }
  if IsSet(initialValue) {
    accumulator := initialValue + this[1]
    i := 1
  } else {
    accumulator := this[1]
    i := 0
  }
  loop l - i {
    accumulator := fn(accumulator, this[A_Index + i], A_Index + i, this)
  }
  return accumulator
}
; 返回字符串,包含数组地址及数组的元素
_ToString(this) {
  ptr := ObjPtr(this)
  prefix := '[ Array@' ptr '] ['
  suffix := ']'
  quote := '"'
  sep := ', '
  for v in this {
    content .= quote v quote sep
  }
  return prefix SubStr(content, 1, content.Length - 2) suffix
}
; 就地反转数组中的元素,并返回同一数组的引用。
_Reverse(this) {
  l := this.Length
  loop l / 2 {
    temp := this[A_Index]
    this[A_Index] := this[l - A_Index + 1]
    this[l - A_Index + 1] := temp
  }
  return this
}
; 反转数组中的元素,并返回新数组的引用。
_ToReverse(this) {
  l := this.Length
  arr := []
  loop l {
    arr.Push(this[l - A_Index + 1])
  }
  return arr
}
; 从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度。
_Shift(this) {
  return this.RemoveAt(1)
}

; # Iterator
; 包装原生Enumrator对象。
; Usage:
;     iter := GetIterator([1, 2, 3, 4])
;     MsgBox iter.Next().key
;     MsgBox iter.Next().value
;     MsgBox iter.Next().done

GetIterator(source) {
  if source is Primitive
    throw TypeError('Expect an object but get the primitive type')
  iter := {}
  iter.DefineProp('Next', { call: Next })
  Next(*) {
    if source is Array or source is Map {
      static enum := source.__Enum()
    } else {
      static enum := source.OwnProps()
    }
    enum.call(&index, &value)
    done := false
    if not (IsSet(index) and IsSet(value))
      index := '', value := '', done := true
    return {
      key: index,
      value: value,
      done: done
    }
  }
  return iter
}