修复J*aScript计算器中操作数未显示问题:构造函数初始化最佳实践


修复javascript计算器中操作数未显示问题:构造函数初始化最佳实践

本教程旨在解决一个常见的J*aScript计算器开发问题:点击按钮后数值无法正常显示。核心问题在于`this.currentOperand`在`appendNumber`函数中未被正确初始化。文章将深入分析问题根源,提供详细的解决方案,即在`Calculator`类的构造函数中调用`this.clear()`方法,确保操作数的初始状态正确,并提供完整的代码示例和最佳实践建议,帮助开发者构建更健壮的Web计算器应用。

深入理解J*aScript计算器中的显示问题

在构建基于J*aScript的计算器应用时,开发者可能会遇到一个常见的问题:当用户点击数字按钮时,期望的数值并未显示在屏幕上。经过调试,通常会发现错误信息指向appendNumber函数中对this.currentOperand的操作,例如Cannot read properties of undefined (reading 'toString')。这表明在尝试将数字追加到当前操作数之前,this.currentOperand的值为undefined。

问题的根本原因在于Calculator类的实例被创建时,其内部状态变量如currentOperand和previousOperand并未被初始化。虽然代码中定义了clear()方法来设置这些变量的初始值(空字符串和undefined),但在Calculator的构造函数中并未调用此方法,导致在首次调用appendNumber时,this.currentOperand处于未定义状态。

具体来说,当执行以下代码行时:

this.currentOperand = this.currentOperand.toString() + number.toString()

如果this.currentOperand是undefined,尝试调用undefined.toString()将导致运行时错误,从而中断程序的执行,使得数字无法显示。

解决方案:构造函数中的状态初始化

解决此问题的最直接且推荐的方法是在Calculator类的构造函数中显式地调用this.clear()方法。clear()方法负责将计算器的所有内部状态变量重置为它们的初始值,包括currentOperand、previousOperand和operation。通过在构造函数中调用它,我们可以确保Calculator类的任何新实例在创建时都处于一个已知的、干净的初始状态。

度加剪辑 度加剪辑

度加剪辑(原度咔剪辑),百度旗下AI创作工具

度加剪辑 359 查看详情 度加剪辑

修改后的Calculator类构造函数应如下所示:

class Calculator {
    constructor(previousOperandTextElement, currentOperandTextElement) {
        this.previousOperandTextElement = previousOperandTextElement;
        this.currentOperandTextElement = currentOperandTextElement;
        this.clear(); // 关键的修复:在构造函数中初始化状态
    }

    // ... 其他方法保持不变
}

通过添加this.clear()这一行,当const calculator = new Calculator(...)被执行时,currentOperand将被设置为一个空字符串(''),而不是undefined。这样,后续对appendNumber的调用将能够安全地执行字符串拼接操作,从而正确显示数字。

完整代码示例

为了提供一个完整的上下文,以下是修正后的J*aScript代码,以及配套的HTML和CSS代码。

J*aScript (script.js)

class Calculator {
    constructor(previousOperandTextElement, currentOperandTextElement) {
        this.previousOperandTextElement = previousOperandTextElement;
        this.currentOperandTextElement = currentOperandTextElement;
        this.clear(); // 确保在实例化时初始化计算器状态
    }

    clear() {
        this.currentOperand = '';
        this.previousOperand = '';
        this.operation = undefined;
    }

    delete() {
        this.currentOperand = this.currentOperand.toString().slice(0, -1);
    }

    appendNumber(number) {
        if (number === '.' && this.currentOperand.includes('.')) return;
        this.currentOperand = this.currentOperand.toString() + number.toString();
    }

    chooseOperation(operation) {
        if (this.currentOperand === '') return;
        if (this.previousOperand !== '') {
            this.compute();
        }
        this.operation = operation;
        this.previousOperand = this.currentOperand;
        this.currentOperand = '';
    }

    compute() {
        let computation;
        const prev = parseFloat(this.previousOperand);
        const current = parseFloat(this.currentOperand);
        if (isNaN(prev) || isNaN(current)) return;
        switch (this.operation) {
            case '+':
                computation = prev + current;
                break;
            case '-':
                computation = prev - current; // 修正:原代码为prev + current
                break;
            case '*':
                computation = prev * current; // 修正:原代码为prev + current
                break;
            case '÷':
                computation = prev / current; // 修正:原代码为prev + current
                break;
            default:
                return;
        }
        this.currentOperand = computation;
        this.operation = undefined;
        this.previousOperand = '';
    }

    getDisplayNumber(number) {
        const stringNumber = number.toString();
        const integerDigits = parseFloat(stringNumber.split('.')[0]);
        const decimalDigits = stringNumber.split('.')[1];
        let integerDisplay;
        if (isNaN(integerDigits)) {
            integerDisplay = '';
        } else {
            integerDisplay = integerDigits.toLocaleString('en', {
                maximumFractionDigits: 0
            });
        }
        if (decimalDigits != null) {
            return `${integerDisplay}.${decimalDigits}`;
        } else {
            return integerDisplay;
        }
    }

    updateDisplay() {
        // 修正:确保getDisplayNumber的结果被赋值给innerText
        this.currentOperandTextElement.innerText = this.getDisplayNumber(this.currentOperand);
        if (this.operation != null) {
            // 修正:确保previousOperandTextElement显示完整表达式
            this.previousOperandTextElement.innerText = `${this.getDisplayNumber(this.previousOperand)} ${this.operation}`;
        } else {
            this.previousOperandTextElement.innerText = '';
        }
    }
}

const numberButtons = document.querySelectorAll('[data-number]');
const operationButtons = document.querySelectorAll('[data-operation]');
const equalsButton = document.querySelector('[data-equals]');
const deleteButton = document.querySelector('[data-delete]');
const allClearButton = document.querySelector('[data-all-clear]');
const previousOperandTextElement = document.querySelector('[data-previous-operand]');
const currentOperandTextElement = document.querySelector('[data-current-operand]');

const calculator = new Calculator(previousOperandTextElement, currentOperandTextElement);

numberButtons.forEach(button => {
    button.addEventListener('click', () => {
        calculator.appendNumber(button.innerText);
        calculator.updateDisplay();
    });
});

operationButtons.forEach(button => {
    button.addEventListener('click', () => {
        calculator.chooseOperation(button.innerText);
        calculator.updateDisplay();
    });
});

equalsButton.addEventListener('click', button => {
    calculator.compute();
    calculator.updateDisplay();
});

allClearButton.addEventListener('click', button => {
    calculator.clear();
    calculator.updateDisplay();
});

deleteButton.addEventListener('click', button => {
    calculator.delete();
    calculator.updateDisplay();
});

注意: 在上述compute()方法中,原代码的减法、乘法和除法操作都错误地使用了加法 (prev + current)。在提供的修正代码中已将其更正为正确的运算符。同时,updateDisplay()方法也进行了优化,确保getDisplayNumber的返回值被正确应用到显示元素上。

CSS (styles.css)

*, *::before, *::after {
    box-sizing: border-box;
    font-family: Arial Black, sans-serif;
    font-weight: normal;
}

body {
    padding: 0;
    margin: 0;
    background: linear-gradient(to right, #00AAFF, #99e016);
}

.calculator-grid {
    display: grid;
    justify-content: center;
    align-content: center;
    min-height: 100vh;
    grid-template-columns: repeat(4,100px);
    grid-template-rows: minmax(120px,auto) repeat(5,100px);
}

.calculator-grid > button {
    cursor: pointer;
    font-size: 2rem;
    border: 1px solid white;
    outline: none;
    background-color: rgba(255,255,255,.75);
}

.calculator-grid > button:hover {
    cursor: pointer;
    font-size: 2rem;
    border: 1px solid white;
    outline: none;
    background-color: rgba(255, 255, 255, 0.95);
}

.span-two {
    grid-column: span 2;
}

.output {
    grid-column: 1/-1;
    background-color: rgba(0,0,0,.75);
    display: flex;
    align-items: flex-end;
    justify-content: space-around;
    flex-direction: column;
    padding: 10px;
    word-wrap: break-word;
    word-break: break-all;
}

.output .previous-operand {
    color: rgba(255,255,255,.75);
    font-size: 1.5rem;
}

.output .current-operand {
    color: white;
    font-size: 2.5rem;
}

HTML (index.html)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Calculator</title>
    <link href="styles.css" rel="stylesheet">
    <script src="script.js" defer></script>
</head>
<body>
    <div class = "calculator-grid">
        <div class="output">
            <div data-previous-operand class="previous-operand"></div>
            <div data-current-operand class="current-operand"></div>
        </div>
        <button data-all-clear class="span-two">AC</button>
        <button data-delete>DEL</button>
        <button data-operation> ÷ </button>
        <button data-number> 1 </button>
        <button data-number> 2 </button>
        <button data-number> 3 </button>
        <button data-operation> * </button>
        <button data-number> 4 </button>
        <button data-number> 5 </button>
        <button data-number> 6 </button>
        <button data-operation> + </button>
        <button data-number> 7 </button>
        <button data-number> 8 </button>
        <button data-number> 9 </button>
        <button data-operation> - </button>
        <button data-number> . </button>
        <button data-number> 0 </button>
        <button data-equals class="span-two">=</button>
    </div>
</body>
</html>

注意事项与总结

  1. 构造函数初始化是关键: 在面向对象编程中,确保类的实例在创建时处于一个有效且定义良好的状态至关重要。将初始化逻辑(如clear()方法)放在构造函数中,可以避免因未定义状态而导致的运行时错误。
  2. 避免隐式undefined: 尽管J*aScript允许变量在声明后不赋值而默认为undefined,但在进行字符串拼接、数学运算等操作前,最好显式地将其初始化为预期的类型(如空字符串''或数字0),以增强代码的健壮性。
  3. 代码审查与测试: 定期审查代码,尤其是在复制或改编他人代码时,是发现这类初始化问题的有效方法。结合单元测试和集成测试,可以更早地捕获和修复潜在的错误。
  4. 操作符逻辑检查: 在本案例中,除了初始化问题,还发现compute方法中的减、乘、除操作符逻辑错误。这提醒我们,即使是看似简单的逻辑也需要仔细检查,确保其正确性。

通过在Calculator类的构造函数中调用this.clear(),我们不仅解决了数字无法显示的问题,还提升了代码的质量和可靠性。这是一个在J*aScript类设计中值得遵循的良好实践。

以上就是修复J*aScript计算器中操作数未显示问题:构造函数初始化最佳实践的详细内容,更多请关注其它相关文章!


# css  # 网站跳转推广  # 将其  # 双击  # 网页设计  # 未被  # 空字符串  # 建设部网站配色复古  # 做网站优化要花多少钱  # 运算符  # 上海短视频seo技巧  # 长葛seo霸屏推广  # 南阳新站seo关键词排名软件  # 运城精美网站建设  # 长安区企业网站推广案例  # 清远市seo搜索优化  # 惠州网站制作推广公司  # 但在  # 面向对象  # 是在  # 器中  # 面向对  # switch  # edge  # app  # seo  # git  # js  # html  # java  # word  # javascript 


相关栏目: 【 Google疑问12 】 【 Facebook疑问10 】 【 优化推广96088 】 【 技术知识133117 】 【 IDC资讯59369 】 【 网络运营7196 】 【 IT资讯61894


相关推荐: 《下一站江湖2》大雪山加入方法  163邮箱网页版官方登录入口 163邮箱网页版访问页面  Go反射进阶:访问内嵌结构体中的被遮蔽方法  《via浏览器》强制缩放网页设置方法  拷贝漫画2025网页版入口 拷贝漫画官网免费看全集  C++中的explicit关键字有什么作用_C++类型转换控制与explicit使用  如何取消数字签名  《百度畅听版》关闭兴趣推荐方法  《i莞家》修改昵称方法  使用CSS :has() 选择器实现父元素样式控制:从子元素反向应用样式  Go语言中方法接收器的选择:值类型还是指针类型?  在VS Code中进行数据科学和机器学习开发  猫眼电影app怎么查询电影院的营业时间_猫眼电影影院营业时间查询教程  《书耽》更换手机号方法  word表格如何按某一列内容进行排序_Word表格按列排序方法  创建您的便携版VS Code:让配置随身携带  AO3中文入口稳定分享_AO3官网HTTPS看文详解  《360浏览器》自动保存账号密码设置方法  《七读免费小说》开通会员方法  macosmonterey系统外接显示器驱动怎么安装_macosmonterey外接显示器驱动与分辨率调整  163邮箱网页版入口 163邮箱在线使用  VB表达式书写规则解析  composer licenses 命令:如何检查项目依赖的许可证?  PHP动态导航按钮:根据用户登录状态切换链接与文本  diskgenius分区工具如何设置Bios启动项  晨报|开发商暗示《空洞骑士:丝之歌》DLC开发中 《合金装备4》有望重制  空腹吃苹果好吗 苹果空腹摄入指南  网页版网易云音乐入口_网易云音乐在线官网登录  WooCommerce 购物车:始终显示所有交叉销售商品  苹果手机如何清理系统缓存数据 iPhone非越狱清理垃圾文件的技巧【系统优化】  Composer reinstall命令重装损坏的包  PySimpleGUI中实现键盘按键与按钮事件绑定教程  创客贴登录页面入口 创客贴网页版最新网址链接  荣耀盒子应用管理技巧  CSS动画如何实现图标旋转并放大_transform rotate scale @keyframes实现  J*aScript 数值去小数位处理:多种方法与实践  C#中的Record类型有什么优势?C# 9新特性Record与Class的用法区别  阿里旺旺电脑网页版入口 阿里旺旺电脑版网页登录入口  win11讲述人怎么关闭 Win11屏幕朗读辅助功能禁用方法【技巧】  《爱笔思画x》涂色教程  多闪APP官方下载安装入口_多闪最新版本获取入口  第五人格PC版怎么避免被封号_第五人格PC版防封号注意事项  《密马》发布账号方法  ExcelSCAN与LAMBDA如何创建自定义移动平均函数_SCAN实现任意窗口期移动平均计算  Win11怎么开启HDR_Windows 11显示器画质增强设置  search中maxlength属性用法解析  Animex动漫社正版在线入口 Animex动漫社动漫官方观看网  电脑“无法访问指定设备、路径或文件”怎么办?五种权限设置方法  优化Flask模板中SQLAlchemy查询迭代标签:处理字符串空格问题  使用TinyButStrong生成HTML并结合Dompdf创建PDF教程 

 2025-11-16

了解您产品搜索量及市场趋势,制定营销计划

同行竞争及网站分析保障您的广告效果

点击免费数据支持

提交您的需求,1小时内享受我们的专业解答。

运城市盐湖区信雨科技有限公司


运城市盐湖区信雨科技有限公司

运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。

 8156699

 13765294890

 8156699@qq.com

Notice

We and selected third parties use cookies or similar technologies for technical purposes and, with your consent, for other purposes as specified in the cookie policy.
You can consent to the use of such technologies by closing this notice, by interacting with any link or button outside of this notice or by continuing to browse otherwise.