VimL 基础对象模型教程

September 23, 2018 · View on GitHub

VimL 语言其实并没有内置的对象概念。其变量类型主要有两种简单标量(数字与字符串) 与两种集合类型(列表与字典)。但在字典的键中不仅可存普通变量,还能存函数(实际 是函数引用 Funcref),因而字典就可以当作对象来使用了。

字典当作对象使用 (dict as object)

字典的常规定义与使用如:

let dic = {'x': 3, 'y': 4}
echo dic['x']
echo dic['y']

但是,也可写成这样:

let obj = {}
let obj.x = 3
let obj.y = 4
echo obj.x + obj.y

这就有点像对象的点号引用语法了。当然了,要用点号引用字典的键,那个键不能用奇怪 字符串,须用正常的能用于变量标志符的字符串。

用字典的键保存函数

用如下语法定义一个函数并保存在字典中:

function! obj.distance() dict
  return sqrt(self.x*self.x + self.y*self.y)
endfunction

与常规函数相比,其函数名不是简单的变量名,而是 obj.distance,表示它是存于字 典 dict 中的键名 distance 中。可以分别用如下命令查看一下这像什么情况:

echo obj.distance
echo obj['distance']
echo obj

函数定义头行末尾的关键字 dict 表示该函数通过字典变量调用,此时函数体中的 self 就代表该字典本身。调用方法如下:

echo obj.distance()
echo obj.distance() * 2

请注意,带括号的 obj.distance() 表示函数调用,它返回函数计算值 5.0。而不带 括号的 obj.distance 等效于 obj['distance'],获取这个键名中所存的东西,它是 个函数引用。

类与实例:实例是类字典的拷贝

可以继续为上面的字典变量 obj 添加更多的属性(键存值)与方法(键存函数)。然 而它始终还只是一个“对象”,如何抽象出更“类”的东西呢。可以这样写:

let class = {}
let class.x = 0
let class.y = 0
function! class.distance() dict
  return sqrt(self.x*self.x + self.y*self.y)
endfunction

let obj1 = copy(class)
let obj1.x = 3
let obj1.y = 4
echo obj1.distance()

let obj2 = copy(class)
let obj2.x = 30
let obj2.y = 40
echo obj2.distance()

echo obj1.distance == obj2.distance
echo obj1.distance() == obj2.distance()

上面第一段代码,定义了一个“类” 也即一个名为 class 的字典。为该类定义了两个属 性名为 x y 及一个方法 distance。然后以这个类为模板,创建了两个“实例”对象 。最后两行演示相等性,这两个对象的 distance 方法是相同的,但它们各自调用 distance() 方法产生的结果是不相等的。

从 VimL 角度看,class obj1 obj2 都只是三个不同的字典变量。键名 x y 存的是值,相互独立,键名 distance 保存的函数引用,却是引用同一个函数。

类与对象的动态性

既然类与对象都是字典,那么可以继续为其添加属性(键值)而互不影响。如:

let class.z = 0
let obj1.a = obj1.distance()
let obj2.b = obj2.distance()

function! class.product() dict
  return (self.x*self.x + self.y*self.y)
endfunction

let obj3 = copy(class)
echo obj3.product()
let obj3.x = 3
let obj3.y = 4
let obj3.z = obj3.product()
echo obj3.z

为类 class 新加一个属性 z,但已创建的实例 obj1 obj2 不会有这个属性。 obj1 新加一个 a 属性来保存 disctance() 的计算值,obj2 也不会有 a 属 性。obj2 新加的 b 属性亦然。

特别地,如果为类 class 新定义一个 product 方法,则原来的 obj1 obj2 不 可调用该方法。但若此后再创建一个 obj3,它就可用 product 方法了,因为它从此 刻的 class 字典中拷贝过来了。

继承:子类也是父类的拷贝

如下代码,可以在原来的类基础上创建新类:

let subclass = copy(class)

" 添加新的属性,或覆盖原来的键值定义

let subobj = copy(subclass)

由于 VimL 是弱类型语言,那也就容易实现多态了。比如把许多不同类的实例对象,保存 在一个列表内,对它们调用同一个方法,那显然会根据它们各自保存的函数引用调用相应 的实现方法了。