第8课:JavaScript实战-简易计算器——入门阶段成果验收

努力不息,未来可期!

恭喜来到「JavaScript 魔法学院」入门阶段最后一课!经过前 7 课的学习,你已掌握 JS 核心基础,今天我们将用 100 多行代码 开发功能完整的计算器,包含加减乘除、连续计算、异常处理能力,最终效果可直接加入个人作品集!

一、功能需求与效果预览

1. 核心功能

  • 数字输入:点击按钮或键盘输入数字
  • 四则运算:支持连续计算
  • 特殊操作:清零(AC)、正负号(±)、小数点
  • 异常处理:除零错误提示、输入限制

2. 最终效果

二、完整代码实现

1. HTML 结构

html 复制代码
<div class="calculator">
    <!-- 显示区域 -->
    <div class="display">0</div>
    <!-- 操作按钮 -->
    <div class="buttons">
        <button class="ac">AC</button>
        <button class="sign">±</button>
        <button class="percent">%</button>
        <button class="operator">÷</button>

        <button class="number">7</button>
        <button class="number">8</button>
        <button class="number">9</button>
        <button class="operator">×</button>

        <button class="number">4</button>
        <button class="number">5</button>
        <button class="number">6</button>
        <button class="operator">-</button>

        <button class="number">1</button>
        <button class="number">2</button>
        <button class="number">3</button>
        <button class="operator">+</button>

        <button class="number zero">0</button>
        <button class="dot">.</button>
        <button class="equals">=</button>
    </div>
</div>

2. JavaScript 核心逻辑

js 复制代码
const display = document.querySelector('.display');
const buttons = document.querySelectorAll('button');

let currentNum = '0';     // 当前输入值
let firstNum = null;      // 存储第一个运算数
let operator = null;      // 存储运算符
let resetScreen = false;  // 是否重置显示

// 监听所有按钮点击(事件委托优化)
document.querySelector('.buttons').addEventListener('click', (e) => {
    const btn = e.target;
    if (!btn.matches('button')) return;

    const value = btn.textContent;

    // 处理数字输入
    if (btn.classList.contains('number')) {
        handleNumber(value);
    }

    // 处理运算符
    if (btn.classList.contains('operator')) {
        handleOperator(value);
    }

    // 处理特殊操作
    if (btn.classList.contains('ac')) {
        resetCalculator();
    }

    if (btn.classList.contains('equals')) {
        calculate();
    }

    if (btn.classList.contains('dot')) {
        addDot();
    }

    updateDisplay();
});

// 处理数字输入
function handleNumber(num) {
    if (resetScreen) {
        currentNum = '0';
        resetScreen = false;
    }
    currentNum = currentNum === '0' ? num : currentNum + num;
}

// 处理运算符
function handleOperator(op) {
    if (firstNum === null) {
        firstNum = parseFloat(currentNum);
    } else if (operator) {
        const result = calculate();
        firstNum = result;
    }
    operator = op;
    resetScreen = true;
}

// 执行计算
function calculate() {
    if (operator === null || firstNum === null) return;
    const secondNum = parseFloat(currentNum);
    let result;

    switch (operator) {
        case '+':
            result = firstNum + secondNum;
            break;
        case '-':
            result = firstNum - secondNum;
            break;
        case '×':
            result = firstNum * secondNum;
            break;
        case '÷':
            if (secondNum === 0) {
                alert("不能除以零!");
                resetCalculator();
                return;
            }
            result = firstNum / secondNum;
            break;
    }

    currentNum = result.toString();
    operator = null;
    firstNum = null;
    return result;
}

// 重置计算器
function resetCalculator() {
    currentNum = '0';
    firstNum = null;
    operator = null;
}

// 添加小数点
function addDot() {
    if (!currentNum.includes('.')) {
        currentNum += '.';
    }
}

// 更新显示
function updateDisplay() {
    display.textContent = currentNum.length > 10
        ? parseFloat(currentNum).toExponential(5)
        : currentNum;
}

3. css 样式

css 复制代码
 body {
      font-family: Arial, sans-serif;
      background-color: #f0f0f0;
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100vh;
      margin: 0;
  }

  .calculator {
      background-color: #222;
      border-radius: 10px;
      box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
      width: 200px;
      padding: 20px;
  }

  .display {
      background-color: #333;
      color: #fff;
      font-size: 24px;
      text-align: right;
      padding: 10px;
      border-radius: 5px;
      margin-bottom: 10px;
  }

  .buttons {
      display: grid;
      grid-template-columns: repeat(4, 1fr);
      gap: 10px;
  }

  button {
      background-color: #444;
      color: #fff;
      font-size: 18px;
      border: none;
      border-radius: 5px;
      padding: 10px;
      cursor: pointer;
      transition: background-color 0.2s;
  }

  button:hover {
      background-color: #555;
  }

  button.ac {
      background-color: #ff4d4d;
  }

  button.ac:hover {
      background-color: #ff6666;
  }

  button.equals {
      background-color: #ff8a00;
  }

  button.equals:hover {
      background-color: #ff9933;
  }

  button.zero {
      grid-column: span 2;
  }

三、代码亮点解析

1. 事件委托优化性能

  • 通过给父元素绑定单个点击监听,管理所有按钮事件
  • 用 e.target.matches() 过滤非按钮点击

2. 状态管理

  • 用 firstNum、operator 等变量记录计算过程
  • resetScreen 控制是否清空当前显示

3. 异常处理

  • 除以零检测:弹出提示并重置计算器
  • 输入限制:避免重复小数点、长数字科学计数显示

4. 用户体验细节

  • 连续计算
  • 大数处理:超过 10 位转科学计数法

进阶预告

JavaScript 进阶系列即将开启,包含:

  • ES6+新特性详解
  • 异步编程与 API 调用
  • 模块化与工程化
  • 实战:开发天气应用、电商购物车

回复【JS】获取本课源码+工具包!

相关推荐
程序员三千_7 分钟前
最近爆火的MCP到底是什么?
前端
古时的风筝19 分钟前
暴论:2025年,程序员必学技能就是MCP
前端·后端·mcp
古时的风筝20 分钟前
这编程圈子变化太快了,谁能告诉我 MCP 是什么
前端·后端·mcp
王月lydia24 分钟前
环境变量篇-vue3的H5项目从0到1工程化落地经验篇2
前端
赵要上天25 分钟前
利用TTP协议 ETag + 路由守卫 实现前端发版后通知用户更新得一个方案
前端
李剑一27 分钟前
写一个vitepress新建文章脚本,自动化创建链接,别再手写了!
前端·node.js·vitepress
火星思想27 分钟前
你来说说JavaScript作用域
javascript·ecmascript 6
火星思想27 分钟前
React如何实现时间切片
前端·react.js
小学生豆豆32 分钟前
eslint以及其扩展插件
前端
Electrolux38 分钟前
【前端bug】Safari的选区机制导致的前端@人组件的bug
前端