
1. 自测题设计思路与知识版图1.1 这套自测题到底在测什么VBA学到初级阶段最典型的状态是视频看了一遍又一遍示例代码抄了厚厚一叠遇到具体场景却总觉得哪哪都不对。语法书上的知识点是零散的真正解决问题时要把它们串起来这个“串起来”的能力靠什么训练靠做题而且必须是有针对性的自测题。我设计这套初级自测题的出发点很简单不是考你“背没背过某个属性”而是考你有没有建立 VBA 解决问题的底层反应。比如看到“把不重复的客户名单提取出来”你的第一反应是循环遍历还是字典看到“图片要随单元格大小自动缩放”你是想到 Shape.Width / Height还是想到单元格坐标换算这种反应能力才是初级到中级的分水岭。整套题分为六个知识板块工程结构与作用域、单元格与对象模型、流程控制与业务逻辑、数组与字典、日期文本处理、调试与性能意识。每个板块的题目都不是孤立的后面几道大题往往要同时用到前面几个板块的知识点。如果你能独立完成 70% 以上的题目说明衔接中级阶段没有大问题如果卡在作用域、字典或对象模型上建议先把对应章节重新过一遍不要急着往下学。1.2 各知识维度的权重怎么分配长期带新手的过程中我总结出一个规律入门阶段最容易出问题的反而不是循环和判断而是变量作用域、数据类型、单元格引用的方式这类“看不见的底层逻辑”。所以这套题给每个板块的权重也做了差异化处理。下表是我的参考权重适用于大多数人你可以根据自己的薄弱环节微调知识板块题目数量核心考察点初级目标工程结构与变量作用域4 题Dim/Public/Private、模块与窗体传参不混淆变量生命周期单元格与对象模型4 题Range 引用、图片与单元格联动不写死路径掌握相对引用流程控制与业务逻辑4 题For/For Each/Do、循环变量陷阱能用循环处理“逐行逐列”的问题数组与字典4 题动态数组、ReDim、Dictionary、数组对比知道“大批量数据”该用什么工具日期文本与基础算法4 题CDate、Split、字符串拼接能正确处理各种“脏数据”调试与性能意识5 题F8 调试、StatusBar、错误处理能独立排查 80% 的报错这套分配背后有一个核心逻辑初级阶段的终点不是“会写宏”而是“写完宏之后能自己查错、能优化性能、能应对数据格式的千变万化”。如果你做完后发现自己在调试板块失分很多恭喜你这反而是最有价值的发现——说明你早该把 Debug 工具用起来了这比多背 10 个属性都有用。整份自测题满分 100 分判断题 8 题每题 2 分选择题 8 题每题 3 分代码填空和简答题共 30 分最后两道综合实操题每题 15 分。1.3 做题方式与心态建议这套题不是考试更像是体检。我建议你给自己安排一个连续的两小时电脑打开 Excel旁边放一本笔记模拟真实开发环境来操作。遇到不会的题目先不要急着翻书把题目读三遍把你脑子里能想到的碎片关键词写下来再去注释里找思路——这个“卡住—回忆—对照”的过程本身就是最好的复习。题目里面有几道故意埋了坑比如“在循环体内修改循环变量”“把文本日期直接当字符串比较”“用嵌套循环做大数据量对比”这些都是我在实际开发中见过多次的翻车点放在自测题里就是想让你们在正式写业务代码前先踩一遍。答题时如果发现自己的答案和我的参考答案不一致不要急着觉得自己错了先试着用 F8 单步执行看变量变化让代码替你说话。下面进入正题先看题目。2. 第一阶段自测题正文2.1 工程结构与变量作用域判断题 1在标准模块顶部用Dim x As Integer声明变量则当前模块的所有过程都可以直接读写 x无需重复声明。判断题 2在标准模块中用Public Sub Test()声明一个过程只能在该模块内调用其他模块无法访问。选择题 3用户窗体 UserForm1 上有一个文本框 TextBox1希望在按钮点击后把模块中的变量传给它显示。以下哪种方案最推荐A. 在窗体代码中定义一个 Public 属性通过UserForm1.TextBox1.Value 模块变量直接赋值B. 在标准模块定义 Public 变量窗体加载时直接读取C. 使用全局静态变量 Static 存储D. 在标签的 Tag 属性里存值判断题 4Dim i As Integer和Dim i As Long在 Excel VBA 中如果不赋值初始值都是 0所以两者在计数场景下没有区别。2.2 单元格与对象模型选择题 5要获取“当前工作表中已使用数据区域”的整体范围下列哪种写法更可靠A.Range(A1).CurrentRegionB.ActiveSheet.UsedRangeC.Cells.SpecialCells(xlCellTypeConstants)D.ActiveSheet.Rows代码补全题 6假设 B 列有 20 个商品价格要求把每个价格加 10% 后写回原单元格但不改变单元格的格式。请补全下面这段代码Sub AddPercent() Dim rng As Range Dim cell As Range Set rng Range(B1:B20) For Each cell In rng 你的代码写在这里 Next cell End Sub判断题 7在标准模块中执行Range(A1) Range(A2)等价于“把 A2 的值复制到 A1”。综合实操题 8工作表的 A1:D8 区域内每个单元格都有一张图片图片需要随着单元格宽度和高度的变化自动缩放并且始终保持图片左上角和单元格左上角对齐。请写出实现这个需求的完整 VBA 代码并说明为什么不能直接设置 Shape.Width 单元格宽度。2.3 流程控制与业务逻辑判断题 9在For i 1 To 10循环体内写上i 5程序会陷入死循环。选择题 10有一个销售明细表共 5000 行。要求统计“在华东区且销售额大于 1000 元”的订单数量下列哪种写法效率最高A.If 区域 华东 And 销售额 1000 Then 计数 计数 1B. 先按区域筛选再遍历C.Select Case 区域里再嵌套IfD. 使用WorksheetFunction.CountIfs代码补全题 11使用Do While循环把 A 列从第 1 行到第 100 行输出“第几行”的信息每行写入 B 列。补全关键循环结构Sub FillRowInfo() Dim i As Long i 1 你的循环代码 End Sub2.4 数组与字典判断题 12ReDim Preserve arr(10)之后再执行ReDim Preserve arr(20)此时 arr 的前 10 个元素仍然保留原值。多选题 13以下哪些场景优先考虑使用字典Scripting.Dictionary而不是循环嵌套A. 从两个 10000 行的表格中找出重复项B. 统计一列数据中每个类别出现的次数C. 对一个 5 行小表做逐行计算D. 按关键字快速查找某行是否存在于大数据表代码实操题 14A 列有 8000 行客户名称其中有大量重复请用字典提取“不重复客户名单”并输出到 D 列。要求写出完整代码并解释为什么字典的 Exists 判断比双层循环快。2.5 日期、文本与基础算法判断题 15用If 2024-03-01 2024-3-1 Then判断两个日期大小可以获得正确结果。选择题 16已知单元格 A1 存放文本1.5A2 存放文本3。执行CDbl(A1) CInt(A2)结果为多少A. 4.5B. 2.5C. 3.5D. 运行时错误代码补全题 17现有一段文本苹果,香蕉,橘子,苹果要把其中的重复项去掉并统计剩余种类数。请用 Split 和字典写出关键代码。2.6 调试与性能意识简答题 18写出三种 VBA 调试工具的用途F8 单步执行、立即窗口 Debug.Print、本地窗口。判断题 19在代码中设置Application.StatusBar 正在处理...后工作簿状态栏会一直显示该文本需要手动清除或恢复。判断题 20On Error Resume Next可以让程序在遇到错误时继续执行因此把它放在过程中开头位置是最稳妥的错误处理方式。实操题 21你的宏运行很慢初步怀疑是循环内频繁读写单元格导致的。请写出两种优化思路并用代码说明如何用变量数组消除循环内的单元格读。进阶选做题 22用 VBA 操作 Word 文档时要删除某个空白页最常见的思路有哪些写出一个可行的思路即可不需要写出全部细节。3. 答案解析与考点复盘3.1 工程结构与变量作用域解析判断第 1 题答案是正确。在标准模块顶部用 Dim 声明变量如果不加 Public则默认是模块级变量该模块的所有过程都能访问。但要注意这是“模块级”而非“全局级”其他模块里的过程是访问不到的。想真正全局共享必须用 Public 声明。这个细节在实际开发中经常引发问题尤其是很多人把声明放在了模块顶部以为所有模块都能用结果运行时总是提示“变量未定义”。判断第 2 题答案是错误。Public Sub 是工程级公开过程其他模块完全可以调用。只要保证模块名和过程名不冲突直接写Module1.Test或者先引用该模块即可。如果你只是想在模块内部使用某个过程习惯上写 Private Sub这样能有效避免模块间重名导致的歧义也算是一种自我保护。选择题第 3 题正确答案是 A。虽然 B 和 C 看起来也能实现但窗体与标准模块之间最清晰的数据传递方式是在窗体类中定义 Public 属性或 Public 函数由外部代码通过实例化后的对象引用窗体控件。直接跨模块访问窗体控件虽然是 VBA 允许的但一旦窗体对象被重新初始化引用关系很容易断调试成本高。我个人的习惯是窗体内部的数据交互尽量用窗体自身的属性和方法避免全局变量满天飞这样模块复用性和可读性都会好很多。判断第 4 题答案是错误。两者初始值确实都是 0但 Integer 的最大范围只有 32767Long 是约 21 亿。在 Excel 中每张表至少十几万行数据循环计数器一不小心就超过 Integer 上限轻则异常重则溢出。初级学员最常见的低级错误就是用 Integer 做行号循环我建议一律无脑使用 Long从源头上消灭这一类错误。3.2 单元格与对象模型解析选择题第 5 题正确答案是 B。CurrentRegion只能获得“从指定单元格向外扩展的连续区域”如果表格中间有空行空列它就会戛然而止。UsedRange则返回工作表所有已使用过的单元格范围更符合“已使用数据区域”的描述。SpecialCells(xlCellTypeConstants)只能选中常量单元格包含公式就会漏掉局限性太大。实际项目里我通常会以CurrentRegion为主体但前提是数据必须是连续的如果源数据可能存在断行就得换UsedRange或先做定位。代码补全题 6 有一个经典陷阱。如果直接在cell cell * 1.1这样写格式不会变因为 Value 的赋值不影响 NumberFormat。但如果想做到“不改变格式”单纯的 Value 写入是安全的。参考写法Sub AddPercent() Dim rng As Range Dim cell As Range Set rng Range(B1:B20) For Each cell In rng cell.Value Round(cell.Value * 1.1, 2) Next cell End Sub判断第 7 题正确但前提是你没有开启Application.ScreenUpdating之类影响刷新的设置且在默认引用方式下确实是把 A2 的值写入了 A1。这里有更隐蔽的坑如果你写成Range(A1).Value Range(A2).Value逻辑上毫无悬念而用Range(A1) Range(A2)这种简写虽然可读一旦涉及到单元格格式、批注或数据类型的隐式转换问题就会冒出来。我的建议是宁可多打几个字母把.Value写清楚未指定属性时的默认行为说起来麻烦不如让代码一眼见底。实操题 8 是重点中的重点。很多新人的第一反应是直接写picShape.Width targetCell.Width结果展开后发现图片比例全乱因为 Shape.Width 是图片的整体宽度直接赋值会拉伸变形。正确做法是计算出“单元格宽高比”和“图片宽高比”的差异然后决定按宽度缩放还是按高度缩放并让图片在单元格内居中。基础代码框架Sub ResizePicToCell(pic As Shape, cell As Range) Dim cellW As Double, cellH As Double Dim picW As Double, picH As Double Dim ratio As Double cellW cell.Width cellH cell.Height picW pic.Width picH pic.Height 取最小缩放比例保持图片不变形 ratio WorksheetFunction.Min(cellW / picW, cellH / picH) pic.LockAspectRatio msoTrue pic.Width picW * ratio pic.Height picH * ratio 定位到单元格左上角 pic.Left cell.Left pic.Top cell.Top End Sub这里直接用LockAspectRatio msoTrue后设置 WidthHeight 会自动等比变化比手动算两次更稳。定位时之所以用cell.Left和cell.Top而不是用绝对坐标是因为当表格行高列宽调整后Left/Top 会动态重新计算——图片和单元格绑定的关键就在于此。如果图片不只一张可以遍历ActiveSheet.Shapes然后逐一调用这个函数。这就是热搜里“vba 插入图片等比例自适应单元格大小”的实现思路。3.3 流程控制与业务逻辑解析判断第 9 题答案是错误。For i 1 To 10并不是不可修改的你写成i 5后循环会从当前值继续递增整体循环次数变少提前结束但不会死循环。真正的死循环只可能出在Do Loop或While没写退出条件的场景。这个题的考点是想提醒你循环变量的值可以被修改这既是灵活性也容易埋雷业务代码里不要随意改动它尤其是多重循环时内层循环误改外层计数器查错能查到你怀疑人生。选择题第 10 题正确答案是 A。对 5000 行的数据量来说内存遍历一次是最可控的筛选后再遍历反而涉及到工作表对象的筛选和定位额外开销更大。Select Case适合单变量多分支这里两个条件都要判断用And组合的最简If反而是最清晰高效的。CountIfs只在数据已存在于工作表上时好用如果需要对多列做复杂逻辑或做数据清洗不建议优先考虑。代码补全题 11 给一个夯实基础的写法Sub FillRowInfo() Dim i As Long i 1 Do While i 100 Cells(i, 2).Value 第 i 行 i i 1 Loop End Sub这里踩过的“经典坑”是忘记在循环内部写i i 1导致死循环。很多初学者觉得这是低级失误实际上在处理复杂业务循环时最后忘记更新游标的情况还真不少。调试时一旦发现程序卡住第一件事就是检查循环体内是否有能跳出循环的修改语句。3.4 数组与字典解析判断第 12 题是正确。ReDim Preserve确实可以保留已有元素不过它有一个致命约束只能修改最后一维的大小。如果你有一个二维数组想动态扩展第一维直接用ReDim Preserve会报错。这个知识点是初级到中级常见的障碍。灵活方案是转置数组扩展目标维度再转置回来或者直接用集合、字典这类容器结构从根本上绕开数组维数限制。多选题第 13 题的正确答案是 A、B、D。判断标准很简单当数据量超过几千行并且关键操作是“查找存在性”或“按键聚合”时字典的时间复杂度是 O(1) 级而双层循环是 O(n²) 级。C 选项只有 5 行双层循环看不出任何性能差异没必要引入字典。记住一句话字典是 VBA 里处理大数据量“查重、计数、关联匹配”的默认首选不是魔法只是哈希查找。实操题 14 参考代码Sub ExtractUniqueCustomers() Dim dict As Object Dim arrData As Variant Dim i As Long Dim lastRow As Long Dim resultRow As Long Set dict CreateObject(Scripting.Dictionary) lastRow Cells(Rows.Count, 1).End(xlUp).Row arrData Range(A1:A lastRow).Value resultRow 1 For i 1 To UBound(arrData, 1) Dim key As String key Trim(CStr(arrData(i, 1))) If key And Not dict.Exists(key) Then dict.Add key, 1 Cells(resultRow, 4).Value key resultRow resultRow 1 End If Next i Set dict Nothing End Sub为什么要用Trim和CStr因为单元格里的数据可能是数字、日期或带前后空格直接用原始值做 Key 会出错。字典的 Key 大小写是敏感的如果你的业务不区分大小写最好统一UCase(key)再放进去。这个细节网上那些“简单示例”都不会提但实际工资表、客户表里全是这种脏数据必须提前处理。3.5 日期、文本与基础算法解析判断第 15 题是错误。2024-03-01和2024-3-1作为字符串比较时会逐字符比较 ASCII 码结果并不代表真正的日期先后。你应该用CDate先把文本转换成真正的日期类型再比较。更稳妥的是使用DateValue处理纯日期文本或者用Format统一成标准格式。凡是涉及日期的计算我从来不信任原始文本一律先转换这是避免“一月排到十二月”这种错乱的最基本防线。选择题第 16 题正确答案是 A。CDbl(1.5)得到 1.5CInt(3)得到 3相加为 4.5。这里隐藏了一个大坑CInt对小数会“四舍五入”或按银行家舍入处理所以如果你把3.7转成 Integer结果有可能是 4。CInt 和 CLng 的舍入规则在不同环境会有细微差别我建议尽量用 CDbl 或 CDec 做数值转换除非你明确知道数据一定是整数。代码补全题 17 经典写法Sub DeduplicateText() Dim txt As String Dim arrItems As Variant Dim dict As Object Dim i As Long txt 苹果,香蕉,橘子,苹果 arrItems Split(txt, ,) Set dict CreateObject(Scripting.Dictionary) For i LBound(arrItems) To UBound(arrItems) Dim item As String item Trim(arrItems(i)) If Not dict.Exists(item) Then dict.Add item, 1 End If Next i Debug.Print 剩余种类数: dict.Count Set dict Nothing End SubSplit返回的数组下标默认从 0 开始但保险起见用LBound和UBound遍历是通用做法。输入文本出现全角逗号“”或者空格不规整时上面的Trim能解决一部分但全角逗号需要额外替换。这种情况在做数据清洗时几乎天天遇到各种“看似一样”的分隔符是文本处理里的隐形杀手。3.6 调试与性能意识解析简答题 18 的参考答案F8 单步执行是逐行运行代码配合立即窗口可以实时查看变量值立即窗口快捷键 CtrlG里既可以用 Debug.Print 输出信息也可以直接敲?变量名看值本地窗口会列出当前过程中所有活动变量的当前值和类型。三者配合能解决至少八成初级阶段的代码排查问题。很多新手觉得调试慢其实是还不了解断点怎么打、本地窗口能看到什么只会在代码里加MsgBox弹窗效率低不说还老忘删我强烈建议尽早把这三板斧用起来。判断第 19 题是正确。设置Application.StatusBar后状态栏会一直显示这段文字直到你把它设置为False或才会恢复默认状态。很多宏跑完以后Excel 一直挂着自定义文本用户还以为程序没结束。所以稳妥做法是在过程开头保存状态结束或出错恢复Sub ShowProgress() Dim oldStatus As String oldStatus Application.StatusBar Application.StatusBar 正在处理... 业务代码 Application.StatusBar oldStatus End Sub判断第 20 题是错误。On Error Resume Next是“遇到错误后继续执行下一条语句”但它不会告诉你错误发生了什么很可能会让程序带着错误的数据继续往下跑最终产生更难排查的结果。它适合用在“你明确知道哪些错误可以忽略”的局部场景而不是全程无脑兜底。更稳妥的方式是On Error GoTo ErrHandler在异常分支里读取Err.Number和Err.Description程序既不死给你看也能留下错误日志。实操题 21 的核心优化思路减少单元格读写次数。比如要累计 A 列 1 万行数据的合计与其在循环里频繁sum sum Cells(i, 1)不如一次性把区域读入数组在内存里算完再一次性写回Sub FastSum() Dim arr As Variant Dim total As Double Dim i As Long arr Range(A1:A10000).Value For i 1 To UBound(arr, 1) total total arr(i, 1) Next i Debug.Print total End Sub这里有一个反直觉的点虽然Range.Value也会产生数组拷贝但比起逐单元格去拿值开销依然小得多。要是再配合Application.ScreenUpdating False循环中的界面刷新也被省下来速度又能提升一截。进阶选做题 22 的思路大体有几种查找下一页开头的分页符并删除或者处理 Word 文档末尾的空白段落。搜索引擎里常见的是“通过 Selection 定位到空白页然后用 Delete 删除内容”。更稳定一点的做法是识别分页符或段落标记判断其前后是否都没有内容再执行范围删除。这个选做题能对多少写多少毕竟是跨应用对象的进阶操作初级水平做不出来很正常它的意义在于让你知道 VBA 的能力不仅限于 Excel 内部。4. 判分标准、典型错误与下一阶段路线4.1 判分标准与自查建议答完题后自己先凭印象判个分如果哪题答案是模棱两可的就算错。按百分制划分55 分以下说明基础语法还存在明显亏空先把《VBA 变量、数据类型、运算符》和《单元格常用对象模型》重看一遍不要急着碰数组和字典55 到 75 分基础过关但熟练度不够建议把错题对应的板块再刷一轮练习重点练 Range、数组和字典三块75 到 90 分初级水平已经达标可以直接进入中级阶段学习比如 Pickup 应用、类模块、事件编程90 分以上可以考虑给自己加一道综合题做一个带窗体、带字典、带图片调用的完整小工具检验综合能力。很多学员问我“到底学到什么程度才算合格”我给的标准从来不是“把书看完”而是“给定一个没见过的业务需求你能用 VBA 写出一个能跑、能改、能查错的程序”。这套自测题就是往这个方向靠的。4.2 初级自学路上最常见的错误速查表做完这套题之后有几个错误几乎每年都能在学员代码里看到我整理成速查表方便你后期写代码时自查错误类型典型表现解决方向整数溢出循环到 40000 行时 i 变成负数循环变量一律 Long表达式作用域混淆模块级变量和过程级变量重名Private/Public区分并遵循最小作用域原则单元格方向写死直接写Range(A1)导致表格结构变化时失效善用End(xlUp)、CurrentRegion、行列号变量格式丢失用.Value写入公式/数据时把格式覆盖掉先设置NumberFormat再写入数据状态栏卡死宏结束后一直显示“正在处理中”结束时恢复Application.StatusBar False字典 Key 脏数据客户名带空格查不到重复存入前统一Trim/CStr/UCase循环内写单元格1 万行数据跑了十几秒用数组批量读写错误处理滥用On Error Resume Next放在开头改用On Error GoTo ErrHandler并记录日志4.3 做完这 22 题之后该朝哪个方向前进如果你已经顺利答出大部分题目迈入中级阶段的第一步我建议从以下三个方向里挑一个深化下去。第一个方向是“面向实际业务建工具”。比如把 Excel 里的订单明细做成一键生成 Word 报价单这就会涉及 VBA 的对象模型拓展、文档内容定位和模板填充。第二个方向是“面向性能做优化”把数组、字典、集合彻底吃透学会在 10 万行数据下做快速匹配以后再遇到“文件大、跑得慢”就不会两眼一抹黑。第三个方向是“面向界面做交互”做多窗体的数据管理系统把 UserForm、类模块、事件编程串起来。我自己带人时还有个私藏练习每年年初把去年一整年的销售流水表格用一套宏自动清洗、汇总、生成图表报告。这个任务能逼着你用到日期处理、文本清洗、字典统计、图表对象、错误处理几乎全部核心技能。你把这套自测题的错题吃透再用两三个周末啃下这类综合小项目初级到中级的过渡就基本顺畅了。4.4 一点个人经验这套自测题最早是我给部门新人做入职摸底用的后来发现不少工作了两三年的同事某些细节居然也栽了跟头。所以你现在做错某一题不用焦虑能通过做题把知识盲区强制暴露出来本身就是收获。我自己当年学 VBA 时也走过很长弯路后来调整方法每学一个知识点马上写一段能落地的代码哪怕只是把“给表格加一个合计行”这类小功能写通都比追着教程抄十遍心里踏实。这个习惯我保留到今天遇到不熟悉的 API 或对象方法第一反应不是翻手册而是开一个新工作簿写三行代码验证属性返回值。代码的世界里编译器和你自己写的测试才是最好的老师。