新闻详情

新闻详情

首页 / 资讯中心 / 详情

WPS JS宏教程:彻底搞懂this指向与构造函数自定义类

发布时间:2026/9/28 15:06:04来源:尧图网络
WPS JS宏教程:彻底搞懂this指向与构造函数自定义类
咱们继续聊WPS JS宏。上一课把构造函数和自定义类的基本骨架搭起来了这一课专门把this这个坎儿讲透。不管你是在 WPS 表格里写宏处理报表还是想把重复的单元格操作收拢成一套自己的工具库只要你在用 JS 宏定义类this就是你绕不开的一道门槛。搞懂它你的代码才能从“一堆散函数”变成“能复用、能维护的真正工具类”。先说一个很多新手都会卡住的点你在构造函数里写了一堆this.xxx ...看着像废话但少了this就直接报错或者拿到undefined。为什么因为 JS 里的类尤其是用构造函数模拟的类和我们熟悉的 VBA 类模块、C 类不太一样它没有一个天然的“作用域容器”。每个对象内部的属性、方法必须通过this这个“自指指针”来建立连接。我更愿意把this理解成每个人说话时的“我”——不同的人说“我”指的是不同的人。同一个函数被哪个对象调用函数内的this就指向哪个对象这是理解本文全部内容的地基。1. 为什么在 WPS 宏里要用构造函数自定义类1.1 散装函数和类的本质区别很多初学者的代码长这样上面写一个function getSum()中间写一个function setData()下面再写一个function clearData()十几个函数摊在模块里互相靠全局变量共享数据。这种写法在十来个单元格的简单操作里还能应付一旦你的宏要处理多张工作表、多个数据区域或者要在多个宏之间共享同一组状态问题就来了——全局变量满天飞你根本记不住哪个变量被哪个函数改过。类的作用本质上就是给“数据”和“操作这些数据的方法”找一个固定的家。比如你做一个“成绩统计”的功能需要维护班级、科目、成绩列表这些数据和计算平均分、最高分、及格率这些操作。用函数式写法你得用一堆全局数组和函数名硬扛用类的方式就是var scoreManager new ScoreManager(三班, 数学, dataArray); scoreManager.calcAverage(); scoreManager.outputToSheet();数据和方法被绑定在同一个对象上思路清晰调用方便。而 WPS 的 JS 宏完全支持 JavaScript 的构造函数语法所以你在 VBA 里用Class做的事在 JS 宏里用function加new就能实现。1.2 为什么选构造函数而不是别的写法现在 JavaScript 已经有class关键字了但在 WPS 的 JS 宏环境里class的支持并不像你想象中那么完美。实际测试下来老版本的 WPS 组件对 ES6 的class语法支持时好时坏尤其是你还要配合this使用的时候。构造函数这种function加new的写法是 ES5 时代的标准做法兼容性最稳在任何版本 WPS 里都能跑。这也是我在这套系列课里坚持用构造函数的原因——不是因为它新潮而是因为它足够稳是“写出来一定能跑”的方案。1.3 构造函数定义类的基本模式用构造函数定义类的标准骨架是这么写的function MyClass(param1, param2) { // 1. 用 this 挂载属性 this.prop1 param1; this.prop2 param2; // 2. 用 this 挂载方法 this.myMethod function() { var result this.prop1 this.prop2; // 方法里用 this 拿属性 return result; }; } // 3. 用 new 创建实例 var instance new MyClass(10, 20); console.log(instance.myMethod()); // 30这个模式的核心只有两句话构造函数里的this指向即将创建的新对象对象的属性、方法都要挂在这个this上才能在后续被对象自己访问到。本文后面所有内容都是这两句话的展开。2. this 在构造函数里的完整工作原理2.1 new 关键字背后的四步动作要理解构造函数里的this首先要理解new干了什么。你写var obj new MyClass()的时候JavaScript 引擎在背后做了四件事创建一个全新的空对象内存中给它分配一块空间。把这个空对象的内部原型链连接到MyClass.prototype上。把这个空对象绑定到this上然后执行MyClass的代码。换句话说构造函数里你写的每一次this.xxx ...实际上都是“往那个新空对象里塞东西”。如果构造函数没有主动返回值那new表达式的结果就是那个被填满的空对象赋给obj。所以构造函数本质是一个“初始化函数”它负责把新对象初始化成你想要的样子。this在这整个过程中就是“新对象”的临时代称。2.2 为什么调用内部对象必须用 this这是最容易被忽略的地方。举个例子有人这么写function ExcelRange(sheet, cell) { var currentSheet sheet; var currentCell cell; this.getValue function() { return currentSheet.Range(currentCell).Value2; }; }这里getValue方法里访问的是currentSheet和currentCell看起来没问题。但如果我在另一个方法里需要调用getValue或者我需要在这个类内部重新给某个属性赋值问题就出现了function ExcelRange(sheet, cell) { var currentSheet sheet; // 私有变量 var currentCell cell; this.getValue function() { return currentSheet.Range(currentCell).Value2; }; this.moveTo function(newCell) { currentCell newCell; // 这样写这个新值存哪了 // currentCell 只是构造函数的局部变量和实例没有绑定 }; }moveTo执行后currentCell确实变了但这个变化只存在于“那一次调用函数时生成的那个局部变量上下文”里对象本身没有记录这个变化。下一次调用getValue拿到的还是旧的currentCell不对其实是同一个闭包里的变量被改了但因为属性的方式不一致你会看到混乱的行为。更关键的是如果我把getValue方法赋值给别的变量去调用currentSheet这种闭包变量还能访问但对象之间完全无法共享和覆盖。正确的写法是function ExcelRange(sheet, cell) { this.sheet sheet; // 挂在 this 上的属性实例真正拥有它 this.cell cell; this.getValue function() { return this.sheet.Range(this.cell).Value2; }; this.moveTo function(newCell) { this.cell newCell; // 修改的是对象自己的属性 return this.getValue(); // 方法内部调方法也要加 this }; }这就是“调用内部对象必须用this”这句话的完整含义类的方法内部要访问“这个对象自己”的属性、方法时必须通过this去引用。不用this你访问到的就是局部变量啊全局变量啊闭包变量啊而那些都不属于这个对象的“内部对象”。2.3 this 只是指向的“令牌”不是拷贝再深入一点。this不是什么魔法它只是一个引用令牌。你把这个令牌挂在哪它就指向哪。构造函数执行期间它指向新对象对象方法被调用时它指向调用者。换句话说this是一个动态绑定的东西它和 C 或者 Java 里“类内部自带 this”的概念不太一样它更像一个“调用上下文参数”每次函数调用都会被引擎重新赋值。我打个比方this就像你快递单上的收件人地址栏。同一个快递模板填不同的地址就送到不同的地方。构造函数这个模板被new了几次就填了几次“新对象”的地址。方法被不同的对象调用this就指向不同的对象。所以你不能在函数里预先假设this是谁只能约定“调用的时候谁调用谁接收”。3. 实操思路先拆需求再定类结构3.1 从一个具体场景出发这一课我们别空谈理论干脆做一个最贴近日常需求的例子单元格区域处理类。你在 WPS 表格里写宏最常见的需求无非是读某个区域的数据、改某些单元格的内容、按条件清理、批量填充。这些操作分散着写到处是ActiveSheet.Range(...)非常啰嗦。我们把它封装成一个RegionHandler类属性工作表名、区域地址。方法读取数据、写入数据、批量设置格式、清空内容、按条件筛选。需求拆解清楚后类的结构就出来了。这里最关键的思考是哪些数据是“这个对象自己应该记住的”哪些数据是“调用者临时传进来的”对象自己应该记住的比如它工作在哪张表、它的工作区域是哪个地址就应该挂在this上。临时传进来的比如你让我把某个值写到区域里这个值就是参数。3.2 注意 JS 宏环境的几个现实约束动手写代码之前还得知道 WPS JS 宏环境里 JS 的“脾气”。我测下来的体会是老的 JS 宏引擎是 ES5 为主let、const用起来要看版本最稳的变量声明是var。我自己写宏一般全部var反正 WPS 宏里变量作用域跟着函数走也不会有太复杂的块级作用域需求。console.log可以用在 WPS 的“宏编辑”界面下方的控制台能看到输出调试很方便。直接操作Cells、Range、Value2这些对象用的还是 WPS 的表格对象模型和 VBA 里类似只是外面套的是 JS 语法。中文键名、中文属性名在 JS 里其实合法比如this.姓名但不建议养成这个习惯后续要切回 VBA 或者跟别人共享代码会很别扭。提示如果你写的宏是给别人用的被别人用 VBScript 或者 WPS 自带的任务调用那方法的命名、参数的命名尽量用英文不要依赖中文输入避免在别的子系统里出现编码问题。这是我踩过的一个很实际的坑。4. 手写一个完整的单元格区域处理类4.1 构造函数的定义与属性挂载直接看完整代码我加了详细注释function RegionHandler(sheetName, regionAddress) { // 挂到 this 上的属性成为实例自己的内部对象 this.sheetName sheetName || Sheet1; this.regionAddress regionAddress || A1:C10; // 获取工作表对象每次调用时动态获取避免对象过期 this.getSheet function() { var ws Worksheets.Item(this.sheetName); if (!ws) { throw new Error(工作表不存在 this.sheetName); } return ws; }; // 读取区域的所有值返回二维数组 this.readValues function() { var ws this.getSheet(); // 调用内部方法必须加 this var rng ws.Range(this.regionAddress); var data rng.Value2; // WPS 的 Range.Value2 返回的是二维数组但有些边界情况会返回单值 if (Array.isArray(data)) { return data; } // 如果只有一个单元格包成二维数组统一调用方式 return [[data]]; }; // 把一个二维数组整体写入区域 this.writeValues function(dataArray) { var ws this.getSheet(); var rng ws.Range(this.regionAddress); rng.Value2 dataArray; return true; }; // 清空区域内容 this.clear function() { var ws this.getSheet(); var rng ws.Range(this.regionAddress); rng.Clear(); return true; }; // 把当前区域的内容复制一份返回新的同类对象类似于拷贝 this.clone function() { var newHandler new RegionHandler(this.sheetName, this.regionAddress); return newHandler; }; }这里每一步都在实践“调用内部对象必须用this”getSheet里用this.sheetNamereadValues里调用this.getSheet()clone里用this.sheetName和this.regionAddress。如果你在readValues里不写this.getSheet()而写getSheet()JS 会去全局作用域找这个函数找不到就报ReferenceError。4.2 验证 this 是否正确初始化的调试方法写完类别急着直接用到正式数据上。我建议先开一个空工作表手动造几个数然后写一段测试宏function testRegionHandler() { var handler new RegionHandler(Sheet1, A1:C3); // 先写入测试数据 var testData [ [10, 20, 30], [40, 50, 60], [70, 80, 90] ]; var writeResult handler.writeValues(testData); console.log(写入成功 writeResult); // 再读回来 var readBack handler.readValues(); console.log(读到的第一行 JSON.stringify(readBack[0])); console.log(读到的第三行第二列 readBack[2][1]); // 验证 clone 后的对象不冲突 var handler2 handler.clone(); handler2.regionAddress D1:F3; handler2.writeValues([[1,2,3],[4,5,6],[7,8,9]]); console.log(原对象区域地址没变 handler.regionAddress); }这段测试里最有价值的检查点是最后那个clone之后改地址的操作。如果clone方法里写的是newHandler new RegionHandler(sheetName, regionAddress)也就是既不写this也不传参你会发现handler2.regionAddress是undefined因为函数内部根本拿不到当前对象的属性。而正确写法把this.sheetName传出去之后每个实例都持有自己独立的属性副本改handler2完全不影响handler这就是类的隔离性。4.3 为什么要动态获取工作表对象而不是缓存细心的读者会注意到我在getSheet里每次调用都重新Worksheets.Item(this.sheetName)拿一次工作表对象没有在构造函数里把ws缓存成一个this.ws。这是有意为之。WPS 的对象模型里工作表、单元格这些对象引用在你切换工作表、删除表、或者执行某些视图操作之后可能会失效。你缓存一个this.ws Worksheets.Item(Sheet1)如果用户中途把 Sheet1 重命名成 Sheet1A你的this.ws就指向了一个可能失效的旧引用。动态获取当然是有一点性能损耗但宏处理的数据量通常没到几万行这点损耗可以无视换来的是每次调用都拿最新的工作表引用代码稳定性高很多。这个思路也可以推广到Range上——如果你的方法经常需要重新计算区域地址那就不要缓存Range对象而是缓存那个“区域地址字符串”每次用的时候再生成Range。对象引用容易过期字符串不会过期。5. 实战进阶用自定义类批量处理多张工作表5.1 设计一个报表汇总类的思路光是单区域操作还不够过瘾。我们用同样的this和构造函数思路做一个稍微复杂一点的类多表汇总器。需求是工作簿里有 1 月、2 月、3 月三张表每张表的 B 列是金额我要把这三个表的数据汇总到“汇总”表的指定位置。先拆结构属性数据源表名数组一月、二月、三月、目标表名、目标起始单元格。方法collect(columnIndex)从每张数据表指定列读数据writeSummary(headers, values)把汇总结果写到目标区域。这个类要处理的核心问题就是当你在一个方法里创建了另一个类的实例或者访问当前实例的多个属性时this是不是一直都在“你身边”。5.2 完整代码与关键步骤拆解function SummaryReport(sheetNames, targetSheet) { // 属性记录数据源表名 this.sheetNames (sheetNames sheetNames.length 0) ? sheetNames : [Sheet1]; this.targetSheet targetSheet || 汇总; // 从一张指定的表读取某一列数据 this.readColumn function(sheetName, colIndex) { var ws Worksheets.Item(sheetName); if (!ws) { return []; } // 假设数据从第2行开始第10行结束也可以做成属性 var data ws.Range(ws.Cells(2, colIndex), ws.Cells(10, colIndex)).Value2; // 将列数据从二维数组里抽出来变成一维数组 var result []; for (var i 0; i data.length; i) { result.push(data[i][0]); } return result; }; // 读取所有数据源表指定列返回一个二维数组 // 第一维是不同表第二维是该表的列数据 this.collectAll function(colIndex) { var result []; // 访问实例自己的属性必须用 this for (var i 0; i this.sheetNames.length; i) { var colData this.readColumn(this.sheetNames[i], colIndex); result.push(colData); } return result; }; // 将汇总数据写入目标表 this.output function(summaryData) { var targetWs Worksheets.Item(this.targetSheet); if (!targetWs) { throw new Error(目标工作表不存在 this.targetSheet); } var rows summaryData.length; if (rows 0) { return false; } var cols summaryData[0].length; // 从 A1 开始写也可以做成属性动态指定 var targetRange targetWs.Range(targetWs.Cells(1, 1), targetWs.Cells(rows, cols)); targetRange.Value2 summaryData; return true; }; // 一键完成从每张表读取某列计算合计写入目标表 this.run function(colIndex) { var collected this.collectAll(colIndex); // 调用内部方法this 必须加 // 构造汇总表内容 var outputData []; // 表头 var headerRow [数据源, 合计]; outputData.push(headerRow); for (var i 0; i collected.length; i) { var colData collected[i]; // 一维数组 var sum 0; for (var j 0; j colData.length; j) { var v parseFloat(colData[j]); if (!isNaN(v)) { sum v; } } outputData.push([this.sheetNames[i], sum]); // 使用属性也要 this } var ok this.output(outputData); // 调用内部方法 return ok; }; } // 使用示例 function runSummaryReport() { var report new SummaryReport( [1月, 2月, 3月], 汇总 ); report.run(2); // 汇总 B 列 }这个案例里的run方法同时调用了this.collectAll、this.sheetNames、this.output。如果你哪一行偷懒不写this比如在run里写var outputData ... collectAll(colIndex)那么调用时就会去找全局的collectAll函数妥妥的ReferenceError。这种错误在 WPS 的 JS 宏控制台里很容易定位因为它会直接告诉你“collectAll is not defined”。5.3 为什么说 this 是连接“数据”和“行为”的桥梁在这两个案例中你会发现this把对象的“拥有的数据”和“能做的事情”紧紧地绑在了一起。对象拥有数据表名列表这个属性this.sheetNames才能让它处理不同的表对象拥有collectAll、output这些方法才能输出结果。如果this丢了数据和行为就分家了——行为变成孤独的函数数据变成不知道属于谁的变量。我见过不少新手在宏里写了一大堆var sheetName Sheet1;放在最外层然后每个函数都直接读取这个全局变量。一开始挺方便等代码到几百行你改了某个函数里的sheetName其他所有函数全部跟着变那叫一个酸爽。用类加this之后每个实例可以有不同的sheetName互不干扰即使你同时创建了 3 个对象处理 3 张不同的表每个对象内部的数据都干干净净的。6. this 的四种绑定规则和使用避坑手册6.1 四种绑定场景对照表this的指向不是一个可以死记硬背的固定值它由“函数怎么被调用”决定。我整理了一张实战对照表你在 WPS 宏里写代码的时候随时可以翻调用方式this 指向典型例子构造函数调用new新创建的那个对象new RegionHandler(Sheet1, A1:C3)里的this指向新对象方法调用对象.方法()调用方法的那个对象handler.readValues()里this指向handler普通函数调用直接函数名()全局对象严格模式下是undefined把方法单独取出来var fn handler.readValues; fn();时this就丢了显式绑定call/apply你指定的那个对象handler.readValues.call(otherObj)这张表最值得铭记的一点是第二种和第三种的区别。在handler.readValues()里readValues函数体中的this是handler没毛病。可一旦你写成var fn handler.readValues; fn();这个fn就变成了一个普通函数调用函数体里的this不再指向handler很可能指向全局对象于是this.sheetName就是undefined。这在 WPS JS 宏里很容易踩中比如你把对象方法当回调函数传给某个事件处理或者数组处理时。6.2 为什么回调函数里 this 会丢看一个实际场景。你写了一个类希望遍历数组后调用对象自己的方法function DataProcessor(tag) { this.tag tag || 未命名处理器; this.processOne function(item) { // 这里想用 this.tag console.log(this.tag 处理了 item); }; this.processAll function(items) { // 错误示范直接传方法引用回调时 this 丢失 items.forEach(this.processOne); // 这里会报错或者 undefined }; }forEach的回调函数里调用this.processOne函数体内部的this是forEach提供的不是DataProcessor实例。解决办法有几种最佳的是在类里面把this先保存一份到一个变量习惯上叫var self this或者var _this thisfunction DataProcessor(tag) { var self this; // 先用 self 锁住当前实例 this.tag tag; this.processOne function(item) { console.log(self.tag 处理了 item); }; this.processAll function(items) { // self 是闭包变量无论 this 怎么变self 都指向原实例 for (var i 0; i items.length; i) { self.processOne(items[i]); } }; }那你说这和“必须用 this”矛盾吗不矛盾。self保存的正是构造函数执行那时的this它本质上是this的一个备份。真正要理解的是this是动态的而self通过闭包把这个动态值固定下来。在 WPS JS 宏的老引擎里bind方法不一定处处可用所以var self this是最保底的方案。我自己的习惯是只要构造函数里出现了嵌套函数或者回调函数第一行就写var self this后面所有内部调用都用self这样基本可以杜绝一类顽固 bug。6.3 构造函数返回对象导致 this 失效的怪坑还有一种情况容易被忽略构造函数中如果显式返回了一个对象那么new出来的结果就不是被初始化的那个this而是你返回的那个对象。function WrongClass() { this.name 我应该被保留; this.say function() { console.log(this.name); }; // 返回一个新的普通对象覆盖了 this return { name: 我覆盖了构造过程, broken: true }; } var w new WrongClass(); console.log(w.name); // 我覆盖了构造过程 w.say(); // 这是 undefined不是函数你在 WPS 宏里写类的时候尽量不要让构造函数有返回值。想让某个方法返回一个新建对象像上面的clone那是方法的职责不是构造函数的职责。构造函数只做一件事初始化实例。如果你发现自己写了return {...}多半是设计思路出了问题回头改成在方法里做工厂逻辑。注意JS 构造函数如果返回原始类型数字、字符串不会覆盖this会按原样返回新对象。但要区分“原始类型”和“对象”非常重要。返回数组、日期、普通对象都会覆盖this。所以最高原则就是干脆别 return 东西。6.4 区分 this 和局部变量的边界感有些同学会问那我在构造函数里天天写var currentSheetName sheetName然后方法与属性之间用这个局部变量不也能干活吗能但它的生命周期和作用域会把你绕晕。局部变量var currentSheetName的生命周期是“构造函数那次调用”它进入闭包后被每个方法共享。this.currentSheetName的生命周期是“这个实例对象的生命周期”每个方法通过this读到的是当前实例自己的属性。当你只有一个实例时闭包变量和this属性看起来没区别。但当你创建两个实例且希望两个实例的方法操作不同的表时function Region(sheetName) { var localName sheetName; // 闭包变量 this.objName sheetName; // 实例属性 this.getLocal function() { return localName; }; this.getObj function() { return this.objName; }; } var r1 new Region(Sheet1); var r2 new Region(Sheet2); // 下面这行需要认真体会 var fn1 r1.getLocal; var fn2 r1.getObj;fn1()永远是Sheet1因为localName被闭包牢牢抓住。fn2()大概率是undefined因为this丢了。这就暴露了两个方案的差别闭包变量适合真正的“私有变量”但你要接受它不能被重新赋值除非方法里改它this属性适合对象对外交互的属性但它依赖调用方式。混合使用没有问题关键是你必须清楚每个变量到底落在哪一层。我自己的偏好是对外要暴露、要修改的放this内部临时计算、不需要暴露的用普通局部变量。两者之间不要混淆。7. 常见问题排查实录7.1 报错 “xxx is not defined”这个报错最常见的原因就是在方法里调用内部方法时漏写了this。排查方法找到报错的那一行确认调用的是“当前实例的方法”。看方法调用前面有没有this.没有就加上。如果这个问题出现在回调函数里光加this.还不够得看this是否已经变了需要用var self this锁定。7.2 读到的属性是 undefined但明明构造函数里赋值了这种问题十有八九是你把实例方法单独拿出来了。检查代码里有没有类似var fn object.method; fn();这种情况把调用改成object.method();如果你确实需要把方法作为回调传出去那就用闭包包一层var fn function() { object.method(); };7.3 构造函数里调用了另一个方法里面的 this 到底指向谁在构造函数里this.myMethod()这种调用是安全的因为此刻this还是新对象。不过要慎重的是myMethod内部如果再嵌套函数或者回调那个嵌套函数里的this又会变成新的东西。所以你在构造函数里写一个方法方法里又传回调去处理数组回调里千万别直接this得self。7.4 用 JSON.stringify 验证对象的真实结构我排查这类问题的一个小技巧在console.log里打JSON.stringify(instance)。它会把你对象上挂在this上的属性全部列出来很方便地验证实例里到底有什么。比如var h new RegionHandler(Sheet1, A1:C3); console.log(JSON.stringify(h)); // 输出类似 {sheetName:Sheet1,regionAddress:A1:C3} // 如果你看到没有 key说明属性都没有挂到 this 上。不那么方便的是对象上的方法不会在 JSON 里显示出来因为JSON.stringify会忽略函数属性。不过没关系我们本来就是要验证属性数据。想看方法列表可以直接for (var key in h) console.log(key)函数名也会出现。7.5 常见问题速查表症状原因解决方案xxx is not defined方法内部漏写this.给内部方法调用加上this.属性是undefinedthis指向已经丢失用对象.方法()调用不要单独取方法引用回调里this错了嵌套函数改变了 this构造函数最上层var self this回调里用self实例方法之间数据不一致属性没挂到this上用了闭包局部变量统一对外属性用this.属性名构造函数 return 对象后实例没方法return 对象覆盖了 this去掉构造函数里的 return 对象8. 关于“拷贝构造函数”和“重载”在 JS 宏里的差异热词里有人提到“拷贝构造函数和重载”这是 C 的概念。需要给大家提个醒在 WPS JS 宏的 JS 环境里没有像 C 那样的“拷贝构造函数”语法也没有“函数重载”机制。如果你要“拷贝”一个对象自己写一个方法通过读取原对象的属性创建新对象就是我们上面案例里的clone方法。这种拷贝是“浅拷贝”属性引用会共享。如果属性里有嵌套对象拷贝时自己要决定是否深拷贝。JS 没有重载同名函数定义两个后者覆盖前者。你只能通过判断参数的个数或类型来模拟重载效果。这也是为什么我一直强调this属性设计要简单直接的原因JS 宏里的类本来就是轻量级的别把 C 里那套复杂概念硬套过来。你把属性规划得清清楚楚拷贝、扩展都顺手。9. 最后一个值得记住的扩展技巧如果你打算在多个宏文件、多个工作簿之间复用这些自定义类可以把类定义代码单独放在一个 JS 宏模块里然后在其他模块里直接调用因为 WPS JS 宏的全局函数和变量在加载后是互通可达的。不过要注意加载顺序——宏模块需要在真正调用前被加载。我自己是习惯把类定义和测试函数分开类定义模块只放函数和“构造函数”测试模块写调用逻辑这样反复修改测试代码时不会影响类定义文件。还有一个小技巧就是给对象方法统一加一个安全入口。比如在构造函数底部把所有方法用var self this包一层再挂出去这种做法在大型类里面能省很多排查时间。具体写法也不复杂function Demo() { this.name ; var self this; this.init function(v) { self.name v; }; this.show function() { console.log(self.name); }; }这种方式牺牲了一点点灵活性方法内的this不再随调用方式变化但换来的稳定性非常可观。尤其适合在 WPS 宏这种环境里快速开发出不怕调用的工具类。我在实际操作中体会最深的一点是this的指向问题看似是一个语言细节但它直接影响你能否顺畅地用“类”的方式组织宏代码。如果你这一步踏实过了后面处理工作表事件、批量数据、自定义工具栏都会轻松很多。这一课的内容已经不少了建议你先照着RegionHandler的例子敲一遍跑通之后再做改造——比如给这个类加一个filterRows方法按条件删除区域内某些行。改的过程中如果遇到this相关的报错就回来翻这一课的速查表效果比硬背理论好得多。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

火爆硅谷的 Jev:一个“不会说话“的 AI 模型,正在把 Agent 的“判断“从提示词技巧变成原生能力 2026/9/28 20:14:58

火爆硅谷的 Jev:一个“不会说话“的 AI 模型,正在把 Agent 的“判断“从提示词技巧变成原生能力

火爆硅谷的 Jev:一个"不会说话"的 AI 模型,正在把 Agent 的"判断"从提示词技巧变成原生能力摘要:最近有一个叫 Jev 的模型在海外爆火——接入 Vercel AI Gateway 后,24 小时内接近 13% 的付费团队直接采用。它…

阅读更多 →
晋级答辩复盘总抓不住重点?5款录音转写工具横评,帮你选出最顺手的那个 2026/9/28 20:14:58

晋级答辩复盘总抓不住重点?5款录音转写工具横评,帮你选出最顺手的那个

每年职级晋升季,最让人头疼的不是答辩本身,而是答辩后的复盘。一场1-2小时的述职评审,你拿着录音笔录了全程,回头要整理出评委的每个提问、你的每个回答逻辑、待改进的细节——光是听一遍录音再手动记重点,就得花掉3-4…

阅读更多 →
语音、图像、文字全塞进一个主干,多模态大模型这条路真的走得通吗 2026/9/28 20:14:58

语音、图像、文字全塞进一个主干,多模态大模型这条路真的走得通吗

上周我在给内部一个文档系统接多模态能力,原本以为这活儿不复杂——给文本那条线加个图像编码器,再挂个语音模块,各管各的不就完了。结果真动手才发现,我错得挺离谱。真正决定效果的,压根不是"接了几个模块"…

阅读更多 →
纸箱、酸奶盒与一座城:这所深圳双语幼儿园的四岁建筑师 2026/9/28 20:14:58

纸箱、酸奶盒与一座城:这所深圳双语幼儿园的四岁建筑师

周末的大铲湾,海风和煦。按地址导航到深圳明湾幼儿园校区,路过一片工地时,一位四岁孩子突然脱口而出:“Mom, this building is under construction!”家长愣住了——上小班的孩子,不仅识别出眼前的建筑状态&#xff0c…

阅读更多 →
YOLOv9实战:雨雪天气路面状况四分类检测数据集与训练全指南 2026/9/28 20:14:57

YOLOv9实战:雨雪天气路面状况四分类检测数据集与训练全指南

简介:这是一份用于雨雪天气路面状态识别的专用数据集,面向自动驾驶、智能交通与道路安全检测方向的研究者与算法工程师。数据按结冰路面、雪地、下雨湿滑、干燥路面四类典型状态组织,图像均为原始拍摄图片,并已使用YOLOv9完成标注…

阅读更多 →
GEO与传统SEO先投谁?出海获客看AI引荐占比定顺序 2026/9/28 20:14:51

GEO与传统SEO先投谁?出海获客看AI引荐占比定顺序

GEO 和传统 SEO 哪个更值得先投,很多出海团队问错了问题——真正该问的是,你的独立站有没有自然搜索流量打底。没有的话,AI 引荐再热闹也接不住。 GEO 和传统 SEO 优化的是两套完全不同的排名逻辑 传统 SEO 让网页在 Google 搜索结果里排到前…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉