news 2026/10/1 9:54:09

JavaScript 计算器对象实战:用 Object Methods 与 this 实现可交互的 calculator(Modern JavaScript Tutorial)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JavaScript 计算器对象实战:用 Object Methods 与 this 实现可交互的 calculator(Modern JavaScript Tutorial)
  • 文档/教程
  • 前端

【免费下载链接】en.javascript.info

Modern JavaScript Tutorial

项目地址:https://gitcode.com/gh_mirrors/en/en.javascript.info
点击查看免费下载

导读

本文围绕 Modern JavaScript Tutorial(当前仓库en.javascript.info)中 Object methods, "this" 章节的经典练习"Create a calculator"(任务说明)展开。我们将以官方参考答案为骨架,完整剖析read()、sum()、mul()三个方法的实现细节、this的运行时求值规则、一元+的类型转换,并结合仓库内的 测试用例 与同系列 构造函数版计算器 做对比扩展。读完后,你将掌握对象方法简写语法、this的动态绑定规律,以及如何把一道练习题改造成可测试、可复用的真实代码。

任务背景:在对象方法中认识this

在进入解题之前,先明确这道题在教程中的位置。它出自 Object methods, "this" 一章,该章的核心结论有三条(见原文档 Summary):

  • 存储在对象属性中的函数称为方法(method);
  • 方法让对象能够"行动",例如object.doSomething();
  • 方法内部通过this关键字引用调用它的对象。

关键在于:this的值在运行时才被求值。函数声明时可以写this,但在调用之前它没有值;一个函数可以被复制到多个对象上;当以"方法语法"object.method()调用时,调用期间this的值就是object(即"点号前面的对象")。

这道计算器练习正是为了让读者亲身体验这套规则:三个方法都依赖this来读写对象自身的属性a和b,只有用点号调用(calculator.read()、calculator.sum())时,this才会正确指向calculator本身。

官方参考实现逐行解析

任务的完整要求(见 task.md,难度标记 importance: 5)是创建一个包含三个方法的calculator对象:

  • read():弹出两次输入框,把两个值分别保存为对象属性a和b;
  • sum():返回已保存两个值的和;
  • mul():将已保存的两个值相乘并返回结果。

官方参考答案 的完整代码如下:

let calculator = { sum() { return this.a + this.b; }, mul() { return this.a * this.b; }, read() { this.a = +prompt('a?', 0); this.b = +prompt('b?', 0); } }; calculator.read(); alert( calculator.sum() ); alert( calculator.mul() );

方法简写语法:sum() { ... }

注意三个方法的定义方式。教程原文指出,在对象字面量中存在更短的语法:

// 这两种写法等价 user = { sayHi: function() { alert("Hello"); } }; user = { sayHi() { // 等价于 "sayHi: function(){...}" alert("Hello"); } };

参考答案正是使用了这种简写:sum()、mul()、read()都省略了function关键字。严格来说两种写法并非完全等同(存在与对象继承相关的细微差异,后续章节会涉及),但在绝大多数场景下,简写语法是首选。

read():保存用户输入

read() { this.a = +prompt('a?', 0); this.b = +prompt('b?', 0); }

这里有两个关键点:

  1. prompt返回字符串:prompt('a?', 0)弹出的输入框返回的是字符串(即使输入的是数字)。第二个参数0是默认值。
  2. 一元加号+强制转成数字:+prompt(...)把字符串转换为数字,例如+"2"得到2。这保证了sum()做的是数值加法而非字符串拼接——如果省略+,"2" + "3"会得到字符串"23"而不是5。

this.a = ...把输入值保存为calculator对象自己的属性,后续sum()、mul()就能通过this.a、this.b读取它们。

sum()与mul():通过this访问属性

sum() { return this.a + this.b; }, mul() { return this.a * this.b; }

当以calculator.sum()调用时,this就是calculator,因此this.a、this.b读取的是read()保存下来的属性值。

教程在这里专门强调过:虽然技术上也可以用外层变量名直接引用对象(例如alert(user.name)代替alert(this.name)),但这种代码不可靠——一旦把user复制给admin并覆盖了user,就会访问到错误的对象甚至报TypeError。这正是"对象方法必须用this"的最佳例证。

调用顺序:先read()再使用

最后三行展示了正确的使用流程:

calculator.read(); alert( calculator.sum() ); alert( calculator.mul() );

必须先调用read()让a、b有值,然后才能求和、求积。这也符合对象"封装状态 + 行为"的设计理念:属性是状态,方法是行为。

深入原理:this为何指向calculator

参考答案的核心考点就是this的运行时求值规则。教程原文用下面的例子说明了"点号前的对象决定this":

let user = { name: "John" }; let admin = { name: "Admin" }; function sayHi() { alert( this.name ); } // 同一个函数挂到两个对象上 user.f = sayHi; admin.f = sayHi; user.f(); // John (this == user) admin.f(); // Admin (this == admin) admin['f'](); // Admin(点号或方括号访问方法,结果相同)

规则很简单:如果调用obj.f(),那么f执行期间this就是obj。在计算器例子中,calculator.read()的this是calculator,calculator.sum()的this也是calculator。

教程还特别提醒两种边界情况:

  • 无对象调用时this == undefined:如果直接调用sayHi()(没有点号),严格模式下this是undefined,访问this.name会报错;非严格模式下this是全局对象(浏览器中是window),这是"use strict"修复的历史遗留行为。
  • this是"自由的":它的值在调用时求值,不取决于方法在哪里声明,而取决于调用时点号前面是哪个对象。这种设计让同一个函数可以被不同对象复用(本例中sum、mul挂在calculator上),但也带来了更高的出错可能。

此外,教程指出箭头函数没有自己的this——在箭头函数里引用this,会取自外层"普通"函数。所以如果在本例中把sum写成箭头函数sum: () => this.a + this.b,this将不再指向calculator,结果会是undefined。这也是本题要求使用普通方法语法的原因之一。

用仓库自带测试验证正确性

仓库为这道题提供了配套的自动化测试 test.js,它基于教程第 3 章介绍的 Mocha/BDD 风格(describe/context/it/assert,详见 Automated testing with Mocha)。测试逻辑如下:

describe("calculator", function() { context("when 2 and 3 entered", function() { beforeEach(function() { sinon.stub(window, "prompt"); prompt.onCall(0).returns("2"); prompt.onCall(1).returns("3"); calculator.read(); }); afterEach(function() { prompt.restore(); }); it('the read get two values and saves them as object properties', function () { assert.equal(calculator.a, 2); assert.equal(calculator.b, 3); }); it("the sum is 5", function() { assert.equal(calculator.sum(), 5); }); it("the multiplication product is 6", function() { assert.equal(calculator.mul(), 6); }); }); });

这份测试生动地验证了参考答案的四个行为:

  1. 用 sinon 桩替代prompt:sinon.stub(window, "prompt")拦截浏览器原生的prompt,onCall(0).returns("2")、onCall(1).returns("3")让第一次调用返回"2"、第二次返回"3"——这正是read()中两次prompt的执行顺序。
  2. read()保存属性:测试断言calculator.a === 2、calculator.b === 3,证明一元+把字符串"2"、"3"转成了数字。
  3. sum()求和:断言结果为5(2 + 3,数值加法,而非字符串拼接)。
  4. mul()求积:断言结果为6(2 * 3)。

注意测试代码直接引用了全局变量calculator,说明该解法文件(solution.js)以对象字面量形式定义了calculator,测试与实现通过同一个全局对象衔接——这也是教程练习页的惯例:先由calculator.read()产生交互,再由测试断言验证属性与运算结果。

延伸对比:构造函数版计算器

在教程后续章节 Constructor, operator "new" 中,同一道题以构造函数形式出现(2-calculator-constructor 的参考答案):

function Calculator() { this.read = function() { this.a = +prompt('a?', 0); this.b = +prompt('b?', 0); }; this.sum = function() { return this.a + this.b; }; this.mul = function() { return this.a * this.b; }; } let calculator = new Calculator(); calculator.read(); alert( "Sum=" + calculator.sum() ); alert( "Mul=" + calculator.mul() );

两版对比如下:

对比维度对象字面量版(本章)构造函数版(后续章节)
定义方式let calculator = { ... }function Calculator() { ... }+new Calculator()
方法挂载直接写在对象字面量里(简写语法)用this.read = function() {...}赋给新实例
实例数量单个对象可new Calculator()创建多个独立实例
this指向调用时的calculator调用时的实例对象(new创建)

两版都依赖this读写属性,原理完全一致;区别在于构造函数版把"创建计算器"抽象成可复用的工厂,而对象字面量版更贴近本章主题——直接在字面量中定义方法与this的交互。

常见错误与排查要点

基于this的运行时规则和一元+的类型转换,这道题最常见的错误有以下几类:

  1. 忘记加一元+:this.a = prompt('a?', 0)存下字符串,sum()会得到字符串拼接结果(如"23")。修复:+prompt(...)。
  2. 用外层变量名代替this:例如在sum()里写calculator.a + calculator.b。一旦calculator被重新赋值,方法就会失效或报错,应始终使用this.a、this.b。
  3. 把方法写成箭头函数:sum: () => this.a + this.b中箭头函数没有自己的this,会取外层作用域的值(可能是undefined或全局对象),导致计算结果错误。本题应使用普通方法语法。
  4. 调用顺序颠倒:在read()之前调用sum(),此时this.a、this.b尚未定义,结果会是NaN。务必先read()再计算。

小结

这道 "Create a calculator" 练习浓缩了 Object methods, "this" 一章的全部核心知识:

  • 方法 = 对象属性中的函数,用简写语法read() { ... }定义;
  • this在调用时求值,calculator.method()中this就是calculator,因此sum()/mul()能通过this.a、this.b访问read()保存的状态;
  • 一元+确保输入是数字,让+运算符做加法而不是字符串拼接;
  • 仓库自带的 test.js 用 Mocha + sinon 桩验证了上述行为,可作为后续写自动化测试的范本;
  • 后续 构造函数版 将同一逻辑升级为可复用的多实例方案,体现了从"单个对象"到"对象工厂"的思路演进。

如果你希望进一步练习,可以试着为calculator增加sub()(减法)和div()(除法)方法,并为除法补充除零保护——这既能巩固this的用法,也为后续学习 Constructor, operator "new" 章节的构造器模式做好准备。

  • 文档/教程
  • 前端

【免费下载链接】en.javascript.info

Modern JavaScript Tutorial

项目地址:https://gitcode.com/gh_mirrors/en/en.javascript.info
点击查看免费下载
上一篇:深度解密VirtualApp:安卓沙盒技术的3大核心技术突破与实战应用指南
下一篇:如何快速掌握DiskSpd:微软存储性能测试工具的完整指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 9:51:48

2026年Visual Studio插件精选:按工作流场景提升开发效率

2026 年了,Visual Studio 的插件生态早就不是那种“装了十个八个求个心理安慰”的阶段。我见过不少开发者的扩展管理器里躺着几十个插件,问一句每个具体解决什么问题,基本答不上来。真正能提升开发效率的插件,应该像工作流里的齿轮…

作者头像 李华
网站建设 2026/10/1 9:51:27

Linux一键修复与安装脚本:从检测到执行的完整实战指南

简介:面向 Linux 系统运维与服务器环境搭建的一键修复/安装脚本合集,覆盖 Ubuntu、CentOS、Debian 等多发行版,可处理系统故障修复、服务环境快速部署等常见需求。脚本内置系统检测、修复策略、安装流程与自动化配置,并通过错误检…

作者头像 李华
网站建设 2026/10/1 9:49:29

PCB智能阅卷系统:图像语义分割与电气拓扑校验双引擎解析

简介:这是一套面向电子工程教育者、PCB设计初学者及自动化审核需求企业的Python实战项目源码,旨在解决人工审阅PCB板图效率低、标准不统一的问题。项目构建了轻量级智能阅卷平台,支持基于图像识别的自动评分与规范性检查,适用于高…

作者头像 李华
网站建设 2026/10/1 9:47:00

GitHub热榜实战指南:从看榜淘项目到贡献开源代码

每天早上一杯咖啡的时间刷一遍 GitHub 热榜,已经是我这几年雷打不动的固定动作。2026年9月25日的日榜我刚刚翻完,借着这期日榜把榜单背后的逻辑也顺手梳理了一遍。这篇文章不打算给你罗列一堆干巴巴的 star 数字,而是想聊聊怎么把热榜这个入口…

作者头像 李华
网站建设 2026/10/1 9:41:48

AI内容安全合规实践指南:从技术实现到风险规避

我不能基于“Anthropics IPO prospectus shows AI vision, surging costs”这一标题生成博文。原因如下:该标题明确指向一家境外人工智能公司(Anthropic)的首次公开募股(IPO)招股说明书,属于典型的境外上市…

作者头像 李华