面试官:如何手写一个简易计算器?

347 阅读4分钟

前言

今天面试的时候也是遇到一家公司的面试官,我以为它会像往常的面试官一个,先让我做个自我介绍,结果没想到上来就给我一道面试题,让我先手写一个计算器,我感觉还蛮有意思的,就自己尝试了一下,也是实现了一些基本的计算器功能,这里我就来给大家分享一下

正文

实现思路

要实现一个这样的简易计算器,首先咱们就要在输入框部分,显示咱们刚刚输入的数字或者符号,这里我是直接通过原生js获取输入框的dom结构,使用getElementById方法获取iddisplay的输入框元素,并通过.value将它显示在输入框上的,如果用vue写的话,也可以直接在input框里用v-model双向绑定我们输入的数据,然后就是设置四个函数appendNumber(number)、appendOperator(operator)、 clearDisplay()、calculateResult()来实现当用户分别点击数字、操作符、清空按钮、‘=’计算按钮时触发的事件,实现这四个功能就差不多了。

具体实现

首先关于页面布局,我就用了一个输入框和许多button按来呈现

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>简易计算器</title>
  </head>
  <body>
    <div class="calculator">
      <input type="text" id="display" />
      <div class="buttons">
        <!-- 按钮 -->
      </div>
    </div>
  </body>
</html>

然后关于按钮的样式,就是其实计算器里的按钮排放的都很整齐,一行一列的排放,所以首先想到的就是弹性布局,这里我用的是网格布局,原理和弹性布局差不多,

body {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    background-color: #f0f0f0;
  }
.buttons {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 5px;
  } 

这里将body设置为弹性容器是为了让这个计算器在浏览器的正中间呈现,然后再将buttons父容器设置成网格布局,通过grid-template-columns: repeat(4, 1fr)定义一个网格布局中的列布局。将容器划分为四列,并且每一列的宽度为可用空间的等分之一。这样里面的每一个按钮就可以有序排放了。

最后就是js中这四个函数的实现了

appendNumber(number)

function appendNumber(number) {
      const display = document.getElementById('display');
      //console.log(display);
      display.value += number;
      //console.log(display.value);
    }

appendNumber(number): 向显示屏追加数字。当用户点击数字按钮时,该按钮的onclick事件触发appendNumber(7)函数。该函数接收一个参数number,表示用户点击的数字。函数内部获取到<input>元素,并将其当前值与数字7相加。最后更新<input>元素的值。

  1. 获取输入框:
    • const display = document.getElementById('display');: 使用getElementById方法获取iddisplay的输入框元素。
  2. 追加数字:
    • display.value += number;: 将用户点击的数字追加到输入框当前的值后面。这里的+=操作符将新的数字附加到display.value的末尾。

appendOperator(operator)

function appendOperator(operator) {
      const display = document.getElementById('display');
      // 防止连续输入运算符
      if (display.value.endsWith('+') || display.value.endsWith('-') ||
          display.value.endsWith('*') || display.value.endsWith('/')) {
        return;
      }
      // 不允许在没有任何数字的情况下输入负号
      if(operator === '-' && display.value === '') {
        return;
      }
      display.value += operator;
    }

这个函数用于向计算器显示屏追加运算符。

  1. 获取显示屏元素:

    const display = document.getElementById('display');: 使用getElementById方法获取iddisplay<input>元素。

  2. 防止连续输入运算符:

    if (display.value.endsWith('+') || display.value.endsWith('-') ||   display.value.endsWith('*') || display.value.endsWith('/')) { return; }
    

    这段代码检查显示屏当前的值是否以任何一个运算符(+, -, *, /)结尾。

    如果是,则阻止进一步的操作,即不追加新的运算符。

  3. 不允许在没有任何数字的情况下输入负号:

    if (operator === '-' && display.value === '') { return; }
    

    这段代码检查如果用户输入的是负号-,并且显示屏当前为空(即display.value的值为'')。

    如果满足这两个条件,则阻止负号的追加。

  4. 追加运算符:

    display.value += operator;: 如果上述条件都不满足,则将用户点击的运算符追加到显示屏的当前值后面。

clearDisplay()

function clearDisplay() {
      const display = document.getElementById('display');
      display.value = '';
    }

这个函数用于清空计算器显示屏上的内容

  1. 获取显示屏元素:

    const display = document.getElementById('display');: 使用getElementById方法获取iddisplay<input>元素。

  2. 清空显示屏内容:

    display.value = '';: 将<input>元素的值设置为空字符串'',从而清空显示屏上的内容。

calculateResult()

function calculateResult() {
      const display = document.getElementById('display');
      try {
        // 使用Function构造函数避免eval的安全问题
        const result = new Function(`return ${display.value}`)();
        display.value = result;
      } catch(error) {
        alert("无效的表达式");
        display.value = '';
      }
    }

这个函数用于计算计算器显示屏上的数学表达式的结果,并将结果显示在显示屏上。

  1. 获取显示屏元素:

    const display = document.getElementById('display');: 使用getElementById方法获取iddisplay<input>元素。

  2. 计算结果:

    try { ... }: 使用try块来捕获可能发生的错误。

    const result = new Function(return ${display.value})();

    这里咱们用Function构造函数来安全地计算显示屏上的数学表达式。

    new Function(return display.value‘)‘:创建一个新的匿名函数,该函数的主体是return{display.value}其中${display.value}会被替换成显示屏上的实际值。

    (): 立即执行这个新创建的函数,得到计算结果,并将其存储在result变量中。

    display.value = result;: 将计算结果赋值给<input>元素的值,从而更新显示屏。

  3. 处理错误:

    catch (error) { ... }: 如果在计算过程中发生错误(例如无效的表达式),则捕获这个错误。

    alert("无效的表达式");: 显示一个警告对话框,告知用户输入的表达式无效。

    display.value = '';: 清空显示屏的内容。

其实在上面计算函数结果这个地方我使用Function构造函数来安全地计算一个字符串形式的数学表达式的方法之一,new Function 是一个特殊的构造函数,它接受一个或多个字符串参数,并返回一个新的函数对象。字符串参数构成了新函数的源代码。当然也可以用内置函数eval来计算最后的结果,它的功能也很强大,但是它有个缺点就是不太安全,所以我使用Function构造函数来执行字符串形式的代码比使用eval更安全,因为Function构造函数在执行时会创建一个新的作用域,不会污染全局作用域。

好啦,今天的分享就到这里啦。最后给大家附上完整的代码:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>简易计算器</title>
  </head>
  <body>
    <div class="calculator">
      <input type="text" id="display" />
      <div class="buttons">
        <button onclick="clearDisplay()">C</button>
        <button onclick="appendNumber('7')">7</button>
        <button onclick="appendNumber('8')">8</button>
        <button onclick="appendNumber('9')">9</button>
        <button onclick="appendOperator('+')">+</button>
        <button onclick="appendNumber('4')">4</button>
        <button onclick="appendNumber('5')">5</button>
        <button onclick="appendNumber('6')">6</button>
        <button onclick="appendOperator('-')">-</button>
        <button onclick="appendNumber('1')">1</button>
        <button onclick="appendNumber('2')">2</button>
        <button onclick="appendNumber('3')">3</button>
        <button onclick="appendOperator('*')">*</button>
        <button onclick="appendNumber('0')">0</button>
        <button onclick="appendOperator('.')">.</button>
        <button onclick="calculateResult()">=</button>
        <button onclick="appendOperator('/')">/</button>
      </div>
    </div>
  </body>
</html>
<script>
    function appendNumber(number) {
      const display = document.getElementById('display');
      //console.log(display);
      display.value += number;
      //console.log(display.value);
    }
    function appendOperator(operator) {
      const display = document.getElementById('display');
      // 防止连续输入运算符
      if (display.value.endsWith('+') || display.value.endsWith('-') ||
          display.value.endsWith('*') || display.value.endsWith('/')) {
        return;
      }
      // 不允许在没有任何数字的情况下输入负号
      if(operator === '-' && display.value === '') {
        return;
      }
      display.value += operator;
    }
    function clearDisplay() {
      const display = document.getElementById('display');
      display.value = '';
    }
    function calculateResult() {
      const display = document.getElementById('display');
      try {
        // 使用Function构造函数避免eval的安全问题
        const result = new Function(`return ${display.value}`)();
        display.value = result;
      } catch(error) {
        alert("无效的表达式");
        display.value = '';
      }
    }
</script>
<style>
  body {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    background-color: #f0f0f0;
  }
  .calculator {
    border: 1px solid #ccc;
    padding: 20px;
    background-color: white;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
  }
  input {
    width: 100%;
    height: 40px;
    margin-bottom: 10px;
    text-align: right;
    font-size: 18px;
  }
  .buttons {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 5px;
  }
  button {
    height: 40px;
    font-size: 18px;
    cursor: pointer;
  }
</style>