简体   繁体   中英

Calculator - Strange issue with JavaScript eval() and leading zero

I'm working on a Calculator app. I'm collecting the numbers (1,2,3…) and operators (+,-,*,/, =) as parameters from html onclick method and using eval() in JavaScript to compute the expression. Something like this..

<button type="submit" class="button-style" onclick="myfunc('7')" id="seven"> 7 </button>    
<button type="submit" class="button-style" onclick="myfunc('8')" id="eight"> 8 </button>    
<button type="submit" class="button-style" onclick="myfunc('9')" id="nine"> 9 </button>

My JavaScript file:-

function myfunc(para) {
  if (para != "=" && para != "AC" && para != "ERS" && para != "+/-") {
    document.getElementById("user-input").innerHTML += para;
    document.getElementById("user-input").style.display = "block";
  } else if (para === "ERS") {
    document.getElementById("user-input").innerHTML = document
      .getElementById("user-input")
      .innerHTML.slice(0, -1);
  } else if (para === "AC") {
    document.getElementById("user-input").innerHTML = " ";
    document.getElementById("result").innerHTML = " ";
  } else if (para === "+/-") {
    let newvar = document.getElementById("user-input").innerHTML;
    let anothervar = newvar * -1;
    document.getElementById("user-input").innerHTML = anothervar;
  } else {
    let evaldata = document.getElementById("user-input").innerHTML;
    let newevaldata = evaldata.replace(/^0+/, ""); //Omitting the leading zeroes
    let resultData = eval(newevaldata);
    let num = resultData.toFixed(2);
    document.getElementById("result").innerHTML = num;
  }
}

And this is how the calculator looks like:- Calculator

My question:

Everything is working fine except one strange issue.
Whenever I try to add 010+100 it shows the result as 108
or if I try 0000010+1000 the result is 1008 , or 0023+3200 gives 3219 .

How is that possible and how to omit the preceding zeroes, I have tried with a regular expression but it doesn't work. Without having preceding zeros the app works just fine. I am stuck here. Please help me out. Thanks.

Here is a screenshot:- calculation error

Never use eval . It's considered evil specially from a user-input — since it evaluates inside the browser any user-submitted value.

Use new Function instead.

Although (as mistakenly suggested) being an issue of parseInt() — it's not.
parseInt("010") will return the correctly expected 10 10 (base 10) in all modern browsers. There's a deprecation of 0 -prefixed octal literals in strict-mode which can be seen at work by specifically using parseInt , as seen.

The issue is strictly with the 010 Octal representation due to the leading 0 — in "sloppy mode"

 console.log(010) // 8

You could adhere to a more battle-proof syntax by using Strict Mode

 "use strict" console.log(010) // Uncaught SyntaxError: Octal literals are not allowed in strict mode

eval("010 + 100") evaluates the first operand 010 as octal (value 8 in decimal), resulting in 8 + 100 . Just like doing ie: parseInt("011", 8) === 9; // true parseInt("011", 8) === 9; // true by using intentionally the radix 8 .

Even if suggested to use new Function - the issue still remains true

 "use strict"; console.log(new Function("return (010 + 100);")()) // 108

therefore: it's your job as a developer to:
get rid of any leading "0" from your String before passing them to new Function .


Finally, back to your specific code:

  • Use new Function instead of evil (OK, you got that one by now)
  • <button type="submit" should be <button type="button"
  • Don't use HTML-inline on* handlers like onclick="myfunc('1')" . JS should be in one place only, and that's the respective <script> tag or file. Use Element.addEventListener() instead.
  • Don't use document.getElementById("user-input").innerHTML from a contenteditable element. Contenteditable is a total browser mess and we're still waiting for a better API to emerge - and it's an unreliable. Use document.getElementById("user-input").value instead, from an <input> Element.
  • Cache your elements you're planning to reuse beforehand. Querying the DOM on every button click might result in a poor user experience (and a laggy calculator).
  • Don't use IDs in HTML, JS. Imagine you want many calculators (as components ) in a single page; by using IDs in multiple places could raise unwanted bugs singe ID is supposed to be unique .

Given all the hints above, here's how you could proceed:

 // DOM utility functions: const el = (sel, par) => (par || document).querySelector(sel); const els = (sel, par) => (par || document).querySelectorAll(sel); // Calculator: const calculator = (elCalculator) => { const elInput = el("input", elCalculator); const elsButtons = els("button", elCalculator); let isReset = false; function buttonHandler() { const sym = this.textContent, // Button Symbol val = elInput.value; // Value currently on screen let isEQ = sym === "=", isAC = sym === "AC", isDl = sym === "⌫", result = ""; if (isReset) { isReset = false; result = sym; } else if (isDl) { result = val.slice(0, -1) || "0"; } else if (isAC) { result = 0; } else if (isEQ) { try { result = new Function(`return (${val});`)(); } catch { isEQ = false; result = val; } } else { result = (val + sym).replace(/^0(?=[^\.]+$)/, ""); } elInput.value = result; isReset = isEQ; } elsButtons.forEach(elBtn => elBtn.addEventListener("click", buttonHandler)); }; // Init: els(".calculator").forEach(calculator);
 /* QuickReset */ * { margin: 0; box-sizing: border-box; } /* Calculator */.calculator { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.5rem; max-width: 400px; }.calculator > * { padding: 0.5rem 1rem; }.calculator > input { text-align: right; }.calculator > input, .calculator > button:last-child { grid-column-start: 1; grid-column-end: 5; }
 <div class="calculator"> <input type="text" value="0" readonly> <button type="button">(</button> <button type="button">)</button> <button type="button">AC</button> <button type="button">⌫</button> <button type="button">7</button> <button type="button">8</button> <button type="button">9</button> <button type="button">*</button> <button type="button">4</button> <button type="button">5</button> <button type="button">6</button> <button type="button">/</button> <button type="button">1</button> <button type="button">2</button> <button type="button">3</button> <button type="button">+</button> <button type="button">E</button> <button type="button">0</button> <button type="button">.</button> <button type="button">-</button> <button type="button">=</button> </div>

in where, amongst its simplicity, the most interesting parts are:

  • result = new Function( return (${val}); )(); instead of eval()
  • result = (val + sym).replace(/^0(?=[^\.]+$)/, ""); to clear the needed "0" only if needed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM