[TOC] > Mon Apr 26 2021 16:15:18 GMT+0800 (GMT+08:00) 从 VBA 转到 JS宏之后,基本单元格描述的变化: 1. VBA 中你可以用 `[A1]`表示单元格 A1,但在 JS中不可以; ```vb ' VBA [A1] = 5 '可以给A1写入 5 ``` > VB中表示单元格很简单的写法。但到 JS 中就是另一码事了,相当于解构赋值。 ```js // JS [A1] = 5 // 报错 // 编译器会往解构赋值方向去解析表达式,下面这种则是相当于把 5 赋值给变量 A1 [A1] = [5] Console.log(A1) // 5 ``` > 另外,`[]` 符号在 JavaScript 中是数组标记,`[1,2,3]`:一个包含 1,2,3 三个元素的一维数组,相当于 Array(1,2,3) 2. 读取单元格的值要用 Value() 方法;用 Value2 属性给单元格写入数据。Value2 也可以读取值。 ```js function _m_read_Write(){ let a2_value = Range("A2").Value() // 读取值 let a2 = Range("A2").Value2 // 使用 Value2 Range("A1").Value2 = a2_value // 写入内容 } ``` 3. `Range("A1")` 这个写法没什么变化。 4. Range.Item 的写法: ```js // A1的值 Range("A1").Item(1).Value() // 等效于Range("A1") // 单元格顺序 Range("C2:D6").Item(1).Value() // C2:D6 区域内的第一格。 ``` 5. Cells.Item的写法: ```js Cells.Item(1,1).Value() // 使用 列名 Cells.Item(1,"A").Select() // 使用 Item 偏移——不使用行列坐标 Cells.Item(1).Select() ``` ## 单元格区域 前面的例子都是基于整个激活的工作表进行的。Range 和 Cells代表整个工作表的单元格集合。当我们限定了单元格区域后,Item 偏移将从单元格区域内的第一格作为起始偏移点。 ```js function _m_Item_test(){ let myArea = Range("D1:F10") let myCells = myArea.Cells let Va = myArea.Item(1) let Vb = myCells.Item(1) Console.clear() Console.log( Va.Row == Vb.Row ) Console.log( Va.Column == Vb.Column ) } // 行、列偏移将从单元格区域第一格作为起始。不推荐!!会超出限定的单元格区域 function _m_Item_test(){ let myArea = Range("D1:F10") let myCells = myArea.Cells let Va = myArea.Item(1,2)// 只跟 D1 有关系,等效于:Range("D1").Offset(0,1) let Vb = myCells.Item(1,2)// 只跟 D1 有关系,等效于:Range("D1").Offset(0,1) Console.clear() Console.log( Va.Row == Vb.Row ) Console.log( Va.Column == Vb.Column ) } ``` 为什么在没有限定区域时为什么能和整表的坐标重合?Cells.Item(1,1) 正好是 A1,因为在没有限定对象的时候,默认将整个工作表作为单元格区域,这个区域的起始单元格正好是A1。 当你想要让偏移是基于整个工作表的时候,就单用 Cells.Item 即可,此时就有个问题了,单用 Cells.Item 指向的是激活状态的工作表。那要怎么弄呢?跟这里的例子一样,我们只需将工作表的单元格作为 Cells 对象即可: ```js function _m_Cells_test(){ let myRange = Worksheets.Item("Sheet2").Cells // Sheet2 的 Cells 对象,此时 myRange 代表Sheet2 工作表的所有单元格对象集合。 let targetRange = myRange.Item(2,2) Console.log(targetRange.Address()) } ``` ## 单元格比较 在 VBA 中,我们可以直接拿两个单元格进行比较(假设它们的内容都是1,格式一致): ```vb debug.Print Range("A1")=Range("A2") ``` 在 JS 宏中则: ```js // 你需要先获取值才能比较 Range("A1").Value()==Range("A2").Value() // Range("A1") 这个在 JSAPI 中表示的单元格对象,它不会像 VBA 那样自行先读值然后比较,像 VBA 那样写则: Range("A1") == Range("A2") // false 哪怕是下一行那样子的: Range("A1") == Range("A1") // false 虽然表示同一个对象,但对象不等于对象。看看下面的例子: []==[] // false // 但这样是相等的 Object==Object // true ``` ## Range.End 属性 返回一个 Range 对象,该对象代表包含源区域的区域尾端的单元格。等同于按键 End+ 向上键、End+ 向下键、End+ 向左键或 End+ 向右键。Range 对象,只读。 很实用的一个属性,可以帮助我们动态定位。但并不是很准确,它要求很苛刻: 1. 起始单元格是不是非空并不重要,`Range("A1").End(xlDown)` 中“A1” 是不是非空不影响它向下查找。 2. 如果没有非空单元格阻拦,它会走到“尽头”,`Range("A1").End(xlDown)` 中,如果A列往下没有非空单元格,它将返回A列的最后一格(A1048576)。 通常地,为了准确地锁定一个有限大的区域,我会: > 假设我要从某一个单元格向四周(上、下、左、右)寻找最后非空单元格。 * 检查起始单元格临近的四个单元格是不是非空单元格。因为只有跟起始单元格相连的单元格非空,那个方向上才有可能是连片的非空,不然则很有可能跑到“尽头”去或者将其他区域的单元格纳入。 * 如果起始单元格的四周相连的单元格都是空的,则直接从起始单元格开始操作。毕竟很少会有数据跑到最后一格,也不想冒险将其他不相连的区域纳入操作范围。 > 当然了,具体得视情况另行操作。 ## Range.Offset 属性 返回 Range 对象,它代表位于指定单元格区域的一定的偏移量位置上的区域。 这跟 Offset函数很像,但是没有最后的两个参数(width/宽度,height/高度)。 ## Range.Resize 属性 调整指定区域的大小。返回 Range 对象,该对象代表调整后的区域。 名称|必选/可选|数据类型|说明 ---|----|-----|---- RowSize|可选|Variant|新区域中的行数。如果省略该参数,则该区域中的行数保持不变。 ColumnSize|可选|Variant|新区域中的列数。如果省略该参数。则该区域中的列数保持不变。 ```js let rng = Range("A2:B4") rng_row = rng.Rows.Count rng_column = rng.Columns.Count let newrng = rng.Resize(rng_row+1,rng_column+1) Console.clear() Console.log(`rng.Count:${rng.Count}; newrng.Count:${newrng.Count}`) ``` ## Range.Item 属性 移步 【绕坑——那些奇奇怪怪的问题】-> 【 Item 属性快把你逼疯了吧】