第一章 VimL 语言主要特点
1.3 弱类型强作用域
“弱类型”不是 VimL 的特点,是几乎所有脚本语言的特点。准确地说是变量无类型,但值 有类型。创建变量时不必定义类型,直接赋值就行,也可以认为是变量临时获得了值的类 型。关于 VimL 的变量与类型,将在下一章的基础语法中详解。
变量作用域是编程的另一个重要概念,也几乎每个语言都要管理的任务。这里说 VimL 具 有“强作用域”的特点,是指它提供了一种简明的语法,让用户强调变量的作用域范围。
VimL 语言级的作用域 g: l: s: a:
变量作用域的意义是指该变量在什么范围内可见,可被操作(读值或赋值)。在 VimL 中 ,每个变量都可以加上一个冒号前缀,表示该变量的作用域。不过另有两条规则:
- 在一些上下文环境中,可以省略作用域前缀,等效于加上了默认的作用域前缀
- 有一些作用域前缀只在特定的上下文环境中使用。
从 VimL 语言角色看,主要有以下几种作用域:
g:
全局作用域。全局变量就是在当前 vim 会话环境中,在任何脚本,任何 ex 命 令行中都可以引用的变量。所有在函数之外的命令语句,都默认是全局变量。l:
局部作用域。只可在当前执行的函数体内使用的变量,在函数体内的变量默认为 局部变量,l:
局部变量也只能在函数体内使用。s:
脚本作用域。只有当前脚本内可引用的变量,包括该脚本的函数体内。a:
参数作用域。特指函数的参数,在函数体内,要引用传入的实参,就得加上a:
前缀,但定义函数时的形参,不能加a:
前缀。a:
还隐含一个限定是只读 性,即不能在函数体内不能修改参数。
这几种作用域前缀所对应的英文单词,可认为是 global
, local
, script
与 argument
。不过 s:
也可认为是 static
,因为在 C 语言中,static
也表示只 在当前文件中有效的意思。
变量作用域的应用原则:
- 尽量少用全局变量,因为容易混乱语用,难于管理。不过在 ex 命令行或 Ex 模式下 只为临时测试的语句,为了方便省略前缀,是全局变量,当然在此命令中也只能是全 局变量。在写 vim 脚本文件时,若要使用全局变量,不要省略
g:
前缀。同时全局 变量名尽量取得特殊点,比如全是大写,或带个插件名的长变量名,以减少被冲突的 概率。 - 局部变量的前缀
l:
一般可省略。但我建议也始终加上,虽然多敲了两个字符,但 编程的效率来源于思路清晰,不在于多少那几个字符。同时在 VimL 编程时,坚持习 惯了作用域前缀,就能在头脑中无形地加强这种意识,然后对作用域的把握也更加精 准。另外,显然地,在函数体内要引用全局就是必须加上g:
前缀。 - 在写 vim 脚本时,函数外的代码,能用
s:
变量就尽量用s:
变量。对于比较大 的脚本变量(如字典),想对外分享,也宁可先定义为s:
变量,再定义一个全局 可访问的函数来返回这个脚本变量。 - 参数变量,
a:
是语法强制要求,漏写了a:
往往是个错误,(如果它没报错,恰 好与同名局部变量冲突了,那是更糟糕与难以觉察的错误)也是初写 VimL 函数最容 易犯的语法错误。
Vim 实体作用域 b: w: t:
Vim 作为一个可视化的编辑器,给用户呈现的,能让用户交互地操作的实体对象主要有 buffer
(缓冲文件),window
(窗口),tabpage
(标签页)。可以把它们想象为 互有关系的容器:
- 缓冲对应着一个正在编辑中的文件,在不细究的情况下可认为与文件等同。(不过不一 定对应着硬盘上的一个文件,比如新建的尚未保存的文件,以及一些特殊缓冲文件)缓 冲也可认为是容纳着文件中所有文本行的容器,就像是简单的字符串列表了。
- 窗口是用于展示缓冲文件的一部分在屏幕上的容器。Vim 可编辑的文件很大,极有可能 在一个屏幕窗口中无法显示文件的所有内容吧,所以窗口对应于缓冲文件还有个可视范 围。一个窗口在一个时刻只能容纳一个缓冲文件,但在不同时刻可以对应不同的缓冲文 件。
- 标签页是可以同时容纳不同的窗口的另一层更大的容器。原始的
Vi
没有标签页,标 签页是Vim
的扩展功能。标签页极大增强Vim
的可视范围,可认为窗口是平面的 ,再叠上标签页就是(伪)立体的了。 - 一个缓冲文件可以展示在不同的窗口或(与)标签页中。所有已展示在某个窗口(包括 在其他标签页的窗口)的缓冲文件都是“已加载”状态,其他曾经被编辑过但当前不可见 的缓冲文件则是“未加载”状态,不过 Vim 仍然记录着所有这些缓冲文件的列表。
然后,Vim 还有个“当前位置”的概念。也就是光标所在的位置,决定了哪个是“当前缓冲 文件”,“当前窗口”与“当前标签页”。
有了这些概念,对 VimL 中的另外三个作用域前缀 b:
w:
t:
就容易理解了。其意 即指一个变量与特定的缓冲文件、窗口或标签页相关联的,以 b:
举例说明。
b:varname
表示在当前缓冲文件(实体对象)中存在一个名为 "varname" 的变量。- VimL 语句在执行过程中,只能直接引用当前缓冲文件的
b:
变量,如果要引用其他 缓冲文件的变量,要么先用其他命令将目标缓冲文件切换为当前编辑的缓冲文件,或者 调用其他的内置函数来访问。 - 如果一个缓冲文件“消失”了,那么与之关联的所有
b:
变量也消失了。 - 窗口与标签页的“消失”能比较形象与容易地理解,关闭了就算消失了。但 Vim 内部对 缓冲的管理比较复杂,未必是从窗口上不见了就代表“消失”了。
- 不过在一般 VimL 编程中,可暂不必深究缓冲文件什么时候“消失”。只要记着一个
b:
变量必定与一个缓冲文件关联着,不同的缓冲文件使用相同的b:
变量是安全的, 它们互不影响。
作用域前缀其实是个字典
以上介绍的各种作用前缀,不仅是种语法约定的标记,它们本身也是个变量,是可以容纳 保存其他变量的字典类型变量。关于字典,在后续章节再详述。这里只能介绍几个演示示 例来体会一下这种特性。
为了操作环境一致,也假设按上节的“裸装” Vim 启动:(不过其实不太影响,也不必太 拘泥)
$ cd ~/.vim/vimllearn
$ vim -u NONE helloworld.txt
现在已用 vim 打开了一个 helloworld.txt
文件。在命令行输入以下 ex 命令:
: let x = 1
: echo x
: echo g:x
: echo g:.x
: echo g:['x']
: echo g:
你可以每次按冒号进入命令行逐行输入,也可以先进入 Ex
模式,连续输入这几行,效 果是一样的(以后不再注明)。
首先用 :let
命令定义了一个 x
变量,命令行语句默认是全局变量。然后用 :echo
命令使用几种不同写法来引用读取这个变量的值,这几种写法都是等效的。最后 将 g:
当作一个整体变量打印显示。它就是个全局字典,能里面包含了 x
键,值就 是 1
。(如果按正常的 vim 启动,你的 vimrc 以及各种插件可能会提供很多全局变量 ,那么 echo g:
的内容可能很多,不只 x
哟)
然后我们再写个脚本观察下 s:
变量。:e hello2.vim
,输入以下内容并保存:
" File: ~/.vim/vimllearn/hello2.vim
let s:hello = 1
let s:world = 2
let s:hello_world = s:hello + s:world
echo s:
脚本写完了,在 ex 命令行输入以下几条测试下:
: source %
: echo s:
: echo s:hello
可见,: source %
命令能正常执行,脚本内的 :echo s:
打印出了该脚本内定义的 所有 s:
脚本变量。但在命令行直接试图访问 s:
变量则报错了。
在脚本中也可以访问全局变量。可以自行尝试在 hello2.vim
中加入对刚才在命令行定 义的 g:x
变量的访问。不过在实际的编程中,可千万别在脚本中依赖在命令行建立的 全局变量。
然后再测试下 b:
变量,直接在命令行执行以下语句吧:
: let b:x = 2
: echo b:x
: echo x
: echo g:x
: e #
: echo b:x
: echo b:
: echo x
: e #
: echo b:x
: echo b:
: echo x
这里,:e #
表示切换编辑另一个文件。在实际工作中,或者用快捷键 <Ctrl-^>
更 方便,不过在本教程中,为说明方便,采用 ex 命令 :e #
。在本例中,vim 启动时 打开的文件是 helloworld.txt
,后来又编辑了 hello2.vim
;此时用 :e #
命令就 切回编辑 helloworld.txt
了,再执行 :e #
就再次回 hello2.vim
中,这是轮换 的效果。
这个示例结果表明,在编辑 hello2.vim
时定义了一个 b:x
变量,这与全局的的 x
变量是互不冲突的。但是在换到编辑 helloworld.txt
时,b:x
变量就不存在了,因 为并未在该缓冲文件中定义 b:x
变量呀。重新回到编辑 hello2.vim
文件时,b:x
变量又能访问了。这也说明当缓冲文件“不可见”时,vim 内部管理它的对象实体其实并未 “消失”呢。而全局变量 g:x
或 x
是始终能访问的。
最后要指出的是,局部作用域 l:
与参数作用域 a:
不能像 s:
或 b:
这样当作 整体的字典变量,是两个例外。VimL 这样处理的原因,可能一是没必要,二是没效率。 函数体内的局部作用域与参数作用域,太窄,没必要将局部变量另外保存一个字典;而且 有效时间太短,函数在栈上反复重建销毁,额外维护一个字典没有明显好处就不浪费了。 另外若要表示所有函数参数另有一个语法变量 a:000
可实现其功能。
其他特殊变量前缀 $ v: &
这几个符号其实并不是作用域标记。不过既然也是变量前缀,也就一道说明一下,也好加 以区分。
含 $
前缀的变量是环境变量。除了 vim 在启动时会从父进程(如 shell)自动继承一 些环境变量,这些变量在使用上与全局变量没什么区别。不过要谨慎使用,一般建议只读 ,不要随便修改,没必要的话也不要随便创建多余的环境变量。(实际上环境变量与全局 变量的最大区另是环境变量在 ex
命令中会自动展开为当前的具体值,比如可直接使用 :e $MYVIMRC
编辑启动加载的 vimrc
文件。但在 VimL 脚本中将环境变量当作全局 变量使用完全没问题)
含 v:
前缀的变量是 vim 内部提供的预定义常量或变量。用户不能增删这类特殊变量 ,也不能修改其类型与含义。比如 v:true
与 v:false
分别用于表示逻辑值“真”与“ 假”。Vim 所支持的这类 v:
变量往往随着版本功能的增加而增加。从与时俱进的角度 讲,vim 脚本中鼓励使用这类变量增加程序的可读性,但若想兼容低版本,还是考虑慎用 。要检查当前 vim 版本是否支持某个 v:
变量,只要用 :help
命令查阅一下即可。 而且 v:
本身也是个字典集合变量,可用 :echo v:
命令查看所有这类变量。
含 &
前缀的变量表示选项的值,相当于把选项变量化,以便于在 VimL 中编程。所支持 的选项集,也是由 Vim 版本决定的,用户当然无法定义与使用不存在的选项。这部分内 容在后面讲选项设置时再行讨论。