You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
200 lines
12 KiB
200 lines
12 KiB
2 years ago
|
# 第一章 VimL 语言主要特点
|
||
|
|
||
|
## 1.3 弱类型强作用域
|
||
|
|
||
|
“弱类型”不是 VimL 的特点,是几乎所有脚本语言的特点。准确地说是变量无类型,但值
|
||
|
有类型。创建变量时不必定义类型,直接赋值就行,也可以认为是变量临时获得了值的类
|
||
|
型。关于 VimL 的变量与类型,将在下一章的基础语法中详解。
|
||
|
|
||
|
变量作用域是编程的另一个重要概念,也几乎每个语言都要管理的任务。这里说 VimL 具
|
||
|
有“强作用域”的特点,是指它提供了一种简明的语法,让用户强调变量的作用域范围。
|
||
|
|
||
|
### VimL 语言级的作用域 g: l: s: a:
|
||
|
|
||
|
变量作用域的意义是指该变量在什么范围内可见,可被操作(读值或赋值)。在 VimL 中
|
||
|
,每个变量都可以加上一个冒号前缀,表示该变量的作用域。不过另有两条规则:
|
||
|
|
||
|
1. 在一些上下文环境中,可以省略作用域前缀,等效于加上了默认的作用域前缀
|
||
|
2. 有一些作用域前缀只在特定的上下文环境中使用。
|
||
|
|
||
|
从 VimL 语言角色看,主要有以下几种作用域:
|
||
|
|
||
|
1. `g:` 全局作用域。全局变量就是在当前 vim 会话环境中,在任何脚本,任何 ex 命
|
||
|
令行中都可以引用的变量。所有在函数之外的命令语句,都默认是全局变量。
|
||
|
2. `l:` 局部作用域。只可在当前执行的函数体内使用的变量,在函数体内的变量默认为
|
||
|
局部变量,`l:`局部变量也只能在函数体内使用。
|
||
|
3. `s:` 脚本作用域。只有当前脚本内可引用的变量,包括该脚本的函数体内。
|
||
|
4. `a:` 参数作用域。特指函数的参数,在函数体内,要引用传入的实参,就得加上
|
||
|
`a:` 前缀,但定义函数时的形参,不能加 `a:` 前缀。`a:` 还隐含一个限定是只读
|
||
|
性,即不能在函数体内修改参数。
|
||
|
|
||
|
这几种作用域前缀所对应的英文单词,可认为是 `global`, `local`, `script` 与
|
||
|
`argument`。不过 `s:` 也可认为是 `static`,因为在 C 语言中,`static` 也表示只
|
||
|
在当前文件中有效的意思。
|
||
|
|
||
|
变量作用域的应用原则:
|
||
|
|
||
|
1. 尽量少用全局变量,因为容易混乱使用,难于管理。不过在 ex 命令行或 Ex 模式下
|
||
|
只为临时测试的语句,为了方便省略前缀,是全局变量,当然在此命令中也只能是全
|
||
|
局变量。在写 vim 脚本文件时,若要使用全局变量,不要省略 `g:` 前缀。同时全局
|
||
|
变量名尽量取得特殊点,比如全是大写,或带个插件名的长变量名,以减少被冲突的
|
||
|
概率。
|
||
|
2. 局部变量的前缀 `l:` 一般可省略。但我建议也始终加上,虽然多敲了两个字符,但
|
||
|
编程的效率来源于思路清晰,不在于少那几个字符。同时在 VimL 编程时,坚持习
|
||
|
惯了作用域前缀,就能在头脑中无形地加强这种意识,然后对作用域的把握也更加精
|
||
|
准。另外,显然地,在函数体内要引用全局就是必须加上 `g:` 前缀。
|
||
|
3. 在写 vim 脚本时,函数外的代码,能用 `s:` 变量就尽量用 `s:` 变量。对于比较大
|
||
|
的脚本变量(如字典),想对外分享,也宁可先定义为 `s:` 变量,再定义一个全局
|
||
|
可访问的函数来返回这个脚本变量。
|
||
|
4. 参数变量,`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 启动:(不过其实不太影响,也不必太
|
||
|
拘泥)
|
||
|
```bash
|
||
|
$ cd ~/.vim/vimllearn
|
||
|
$ vim -u NONE helloworld.txt
|
||
|
```
|
||
|
|
||
|
现在已用 vim 打开了一个 `helloworld.txt` 文件。在命令行输入以下 ex 命令:
|
||
|
```vim
|
||
|
: 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`,输入以下内容并保存:
|
||
|
```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 命令行输入以下几条测试下:
|
||
|
```vim
|
||
|
: source %
|
||
|
: echo s:
|
||
|
: echo s:hello
|
||
|
```
|
||
|
|
||
|
可见,`: source %` 命令能正常执行,脚本内的 `:echo s:` 打印出了该脚本内定义的
|
||
|
所有 `s:` 脚本变量。但在命令行直接试图访问 `s:` 变量则报错了。
|
||
|
|
||
|
在脚本中也可以访问全局变量。可以自行尝试在 `hello2.vim` 中加入对刚才在命令行定
|
||
|
义的 `g:x` 变量的访问。不过在实际的编程中,可千万别在脚本中依赖在命令行建立的
|
||
|
全局变量。
|
||
|
|
||
|
然后再测试下 `b:` 变量,直接在命令行执行以下语句吧:
|
||
|
```vim
|
||
|
: 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 版本决定的,用户当然无法定义与使用不存在的选项。这部分内
|
||
|
容在后面讲选项设置时再行讨论。
|