英文:
Why does the "eval" function return "undefined"?
问题
I can provide a translation of the text you provided. Here it is:
我是编程的初学者,仍在学习过程中,我在练习JavaScript时遇到了一个问题,无法解决一个简单的计算器应用的问题。
我遇到的问题是,当我在计算器屏幕上输入像3+3这样的提示时,然后按等号时,我会得到结果3+3undefined。
首先,我检查了是否正确选择了变量,然后是否正确分配了监听器,然后尝试使用数学表达式评估,但都没有成功。我尝试的每一种方法都要么引发错误,要么返回undefined。
(以下是JavaScript和HTML代码片段,我不会翻译它们,因为您要求不翻译代码部分。)
如果您有关于代码的具体问题或需要帮助,请随时提出。
英文:
I'm beginner in coding, still in learning process, and I stuck on some problem creating a simple Calculator app as part of my practice in JS.
I have problem that when I make some prompt in the calculator screen as 3+3, than when I press equal I get the result 3+3undefined.
First I checked if I selected the variables correctly, then if I assigned add Listener correctly, then I tried to use math expression evaluation, but all without success. Everything I've tried always either throws Error or undefined.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
(function() {
let screen = document.querySelector('#display');
let buttons = document.querySelectorAll('.btn');
let clear = document.querySelector('#clear');
let equal = document.querySelector('#equals');
buttons.forEach(function(button) {
button.addEventListener('click', function(e) {
let value = e.target.dataset.num;
screen.value += value;
});
});
equal.addEventListener('click', function(e) {
if (screen.value === '') {
screen.value = "";
} else {
let answer = eval(screen.value); /* every check says that there is a code error in this line.*/
screen.value = answer;
}
});
clear.addEventListener('click', function(e) {
screen.value = "";
});
})();
<!-- language: lang-html -->
<div class="calculator">
<form>
<input type="text" class="screen" id="display">
</form>
<div class="buttons">
<button type="button" class="btn btn-yellow" id="multiply" data-num="*">*</button>
<button type="button" class="btn btn-yellow" id="divide" data-num="/">/</button>
<button type="button" class="btn btn-yellow" id="subtract" data-num="-">-</button>
<button type="button" class="btn btn-yellow" id="add" data-num="+">+</button>
<button type="button" class="btn" id="nine" data-num="9">9</button>
<button type="button" class="btn" id="eight" data-num="8">8</button>
<button type="button" class="btn" id="seven" data-num="7">7</button>
<button type="button" class="btn" id="decimal" data-num=".">.</button>
<button type="button" class="btn" id="six" data-num="6">6</button>
<button type="button" class="btn" id="five" data-num="5">5</button>
<button type="button" class="btn" id="four" data-num="4">4</button>
<button type="button" class="btn btn-red" id="clear">C</button>
<button type="button" class="btn" id="three" data-num="3">3</button>
<button type="button" class="btn" id="two" data-num="2">2</button>
<button type="button" class="btn" id="one" data-num="1">1</button>
<button type="button" class="btn btn-green" id="equals">=</button>
<button type="button" class="btn" id="zero" data-num="0">0</button>
</div>
</div>
<script src="./script.js"></script>
<!-- end snippet -->
答案1
得分: 2
你需要测试从按钮的数据属性中获取到实际值。如果使用委托会更容易,你可以直接使用按钮的textContent。我已经完全移除了数据属性。添加了一个清理器。
(function() {
// 去除除了数字、()、-+/* 和 . 之外的所有字符
const sanitizer = str => str.replace(/[^-()\d/*+.]/g, '');
const screen = document.getElementById("display");
document.querySelector(".buttons").addEventListener('click', function(e) {
const tgt = e.target; // 点击的元素
if (tgt.matches("#equals")) {
if (screen.value !== "") { // 更简单的测试条件
try {
let answer = eval(sanitizer(screen.value));
}
catch (e) {
console.log(e.message);
return;
}
screen.value = answer;
}
} else if (tgt.matches("#clear")) {
screen.value = "";
} else {
let value = tgt.textContent;
if (value) screen.value += value;
}
});
})();
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" />
<div class="calculator">
<form>
<input type="text" class="screen" id="display">
</form>
<div class="buttons">
<button type="button" class="btn btn-yellow" id="multiply">*</button>
<button type="button" class="btn btn-yellow" id="divide">/</button>
<button type="button" class="btn btn-yellow" id="subtract">-</button>
<button type="button" class="btn btn-yellow" id="add">+</button><br/>
<button type="button" class="btn">9</button>
<button type="button" class="btn">8</button>
<button type="button" class="btn">7</button>
<button type="button" class="btn">.</button><br/>
<button type="button" class="btn">6</button>
<button type="button" class="btn">5</button>
<button type="button" class="btn">4</button>
<button type="button" class="btn btn-red" id="clear">C</button><br/>
<button type="button" class="btn">3</button>
<button type="button" class="btn">2</button>
<button type="button" class="btn">1</button>
<button type="button" class="btn btn-green" id="equals">=</button><br/>
<button type="button" class="btn">0</button>
</div>
</div>
<script src="./script.js"></script>
英文:
You need to test you get an actual value from the button's data attribute.
It makes it easier if you delegate and you can simply use the textContent of the buttons. I have removed the data attributes completely
Added a sanitiser
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
(function() {
// strip anything other than digits, (), -+/* and .
const sanitizer = str => str.replace(/[^-()\d/*+.]/g, '');
const screen = document.getElementById("display");
document.querySelector(".buttons").addEventListener('click', function(e) {
const tgt = e.target; // what was clicked
if (tgt.matches("#equals")) {
if (screen.value !== "") { // simpler test too
try {
let answer = eval(sanitizer(screen.value));
}
catch (e) {
console.log(e.message);
return;
}
screen.value = answer;
}
} else if (tgt.matches("#clear")) {
screen.value = "";
} else {
let value = tgt.textContent;
if (value) screen.value += value;
}
});
})();
<!-- language: lang-html -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" />
<div class="calculator">
<form>
<input type="text" class="screen" id="display">
</form>
<div class="buttons">
<button type="button" class="btn btn-yellow" id="multiply">*</button>
<button type="button" class="btn btn-yellow" id="divide">/</button>
<button type="button" class="btn btn-yellow" id="subtract">-</button>
<button type="button" class="btn btn-yellow" id="add">+</button><br/>
<button type="button" class="btn">9</button>
<button type="button" class="btn">8</button>
<button type="button" class="btn">7</button>
<button type="button" class="btn">.</button><br/>
<button type="button" class="btn">6</button>
<button type="button" class="btn">5</button>
<button type="button" class="btn">4</button>
<button type="button" class="btn btn-red" id="clear">C</button><br/>
<button type="button" class="btn">3</button>
<button type="button" class="btn">2</button>
<button type="button" class="btn">1</button>
<button type="button" class="btn btn-green" id="equals">=</button><br/>
<button type="button" class="btn">0</button>
</div>
</div>
<script src="./script.js"></script>
<!-- end snippet -->
答案2
得分: 2
你的等号按钮触发了两个事件。一个是button.addEventListener
,另一个是equal.addEventListener
。当点击等号按钮时,它不应该运行按钮代码,因为data-num
不存在。这就是为什么在你的eval中会出现undefined
的原因。
一个更清晰的解决方案是检查数据属性data-num
是否存在,而不是将data-num
添加到等号按钮上。这样可以避免添加不必要的HTML。
buttons.forEach(function(button) {
button.addEventListener('click', function(e) {
if (e.target.dataset.hasOwnProperty('num')) {
let value = e.target.dataset.num;
screen.value += value;
}
});
});
英文:
Your equal button is firing two events.
One for button.addEventListener
and the other equal.addEventListener
. When the equal button is clicked it shouldn't run the button code because data-num
does not exist. This is why undefined
is being raised in your eval.
A cleaner solution is to check if the data attribute data-num
exists instead of adding data-num
to the equal button. Helps avoid adding unnecessary HTML.
buttons.forEach(function(button) {
button.addEventListener('click', function(e) {
if (e.target.dataset.hasOwnProperty('num')) {
let value = e.target.dataset.num;
screen.value += value;
}
});
});
答案3
得分: 0
这段代码修复了您的问题。
"equals button dose not have data-num property trigger undefine" 翻译为 "等于按钮没有data-num属性触发未定义"。
英文:
this code fix your problem
equals button dose not have data-num property trigger undefine
buttons.forEach(function(button) {
button.addEventListener('click', function(e) {
if(e.target.id !="equals"){
let value = e.target.dataset.num;
screen.value += value;
}
});
});
答案4
得分: 0
在您的HTML中的这行代码中:
&lt;button type="button" class="btn btn-green" id="equals"&gt;=&lt;/button&gt;
请添加 data-num=""
&lt;button type="button" class="btn btn-green" data-num="" id="equals"&gt;=&lt;/button&gt;
说明: 您在JS中始终将 data-num 值添加到输入字段中,如果未定义,您将获得诸如 "9+9undefined" 的值,这不能由eval处理。如果添加一个空字符串,相同的值将是 "9+9"。
编辑(问题解决后)
正如您所说,您仍在学习,我认为您的实现和逻辑 看起来都很好。
因此,这只是一个侧面说明: 如果您愿意,您可以让JS更多地完成“绘制”计算器的工作,并以稍微不同的方式监听事件,通过监听body上的点击事件,然后过滤出是否有任何点击实际命中某个按钮(这称为“委托事件处理”)。在JS中解决像简单计算器这样的问题有许多不同的方法,这就是编程变得有趣的地方。如果我赶时间,这是我会做的方式:)
另外: 确保 0.1 + 0.2 返回 0.3,这可能需要一些工作。请参阅:https://stackoverflow.com/questions/588004/is-floating-point-math-broken
<!-- 开始代码片段:js 隐藏:false 控制台:true Babel:false -->
<!-- 语言:lang-js -->
document.body.innerHTML = `
<div class="calculator">
<input type="text" placeholder="0" readonly>`
+ ''789/456*123-C0.+()^=''.split(''')
.map(x => `<div class="btn `
+ `${isNaN(x) && x !== '.' ? 'ctrl' : ''}">`
+ `${x}</div>`).join(''') + ''</div>';
document.body.addEventListener('click', e => {
let btn = e.target.closest('.btn');
if (!btn) { return; }
let input = document.querySelector('input');
let val = btn.innerText;
val === 'C' && (input.value = '');
val === '=' && (() => {
try {
input.value = eval(input.value.replaceAll('^', '**'))
.toFixed(10).replace(/(\.[1-9]*)0{1,}$/g, '$1')
.replace(/\.$/g, '');
}
catch (e) { input.value = ''; }
})();
!'C='.includes(val) && (input.value += val);
});
<!-- 语言:lang-css -->
.calculator {
border-radius: 0.5vw;
background-color: rgb(41, 111, 164);
padding: 10px;
display: inline-block;
}
.calculator::after {
content: "";
display: table;
clear: both;
}
.btn {
width: 3vw;
height: 3vw;
background-color: black;
margin: 1px;
color: white;
float: left;
font-size: 2vw;
font-family: Verdana;
padding: 0.8vw 0.5vw 0.2vw;
text-align: center;
cursor: pointer;
}
.btn:nth-child(4n+2) {
clear: both;
}
.btn.ctrl {
background-color: #333;
}
input {
font-family: Verdana;
font-size: 2vw;
width: 16vw;
height: 3vw;
margin-bottom: 1vw;
display: block;
text-align: right;
}
<!-- 语言:lang-html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Calculator</title>
</head>
<body>
</body>
</html>
<!-- 结束代码片段 -->
英文:
On this line in your HTML:
<button type="button" class="btn btn-green" id="equals">=</button>
Add data-num=""
<button type="button" class="btn btn-green" data-num="" id="equals">=</button>
Explanation: You always add the data-num value to the input field in your JS, if it is undefined you will get a value such as "9+9undefined", and that can't be handled by eval. If you add an emptry string the same value will be "9+9".
Edit (after problem solved)
As you said, you are still learning, and I think your implementation and your logic looks just fine.
So this is only a sidenote: If you want to you can let JS do more of the work of 'drawing' the calculator and also listen to events a little bit differently, by listening to clicks on the body and then filter out if any of the clicks actually hit a certain button (this is called delegated event handling). There are so many flavors of how you solve something like a simple calculator in JS and that's what's makes programming fun. Here's how I would have done it, if in a hurry
Also: Make sure that 0.1 + 0.2 returns 0.3, this may take some work. See: https://stackoverflow.com/questions/588004/is-floating-point-math-broken
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
document.body.innerHTML = `
<div class="calculator">
<input type="text" placeholder="0" readonly>`
+ '789/456*123-C0.+()^='.split('')
.map(x => `<div class="btn `
+ `${isNaN(x) && x !== '.' ? 'ctrl' : ''}">`
+ `${x}</div>`).join('') + '</div>';
document.body.addEventListener('click', e => {
let btn = e.target.closest('.btn');
if (!btn) { return; }
let input = document.querySelector('input');
let val = btn.innerText;
val === 'C' && (input.value = '');
val === '=' && (() => {
try {
input.value = eval(input.value.replaceAll('^', '**'))
.toFixed(10).replace(/(\.[1-9]*)0{1,}$/g, '$1')
.replace(/\.$/g, '');
}
catch (e) { input.value = ''; }
})();
!'C='.includes(val) && (input.value += val);
});
<!-- language: lang-css -->
.calculator {
border-radius: 0.5vw;
background-color: rgb(41, 111, 164);
padding: 10px;
display: inline-block;
}
.calculator::after {
content: "";
display: table;
clear: both;
}
.btn {
width: 3vw;
height: 3vw;
background-color: black;
margin: 1px;
color: white;
float: left;
font-size: 2vw;
font-family: Verdana;
padding: 0.8vw 0.5vw 0.2vw;
text-align: center;
cursor: pointer;
}
.btn:nth-child(4n+2) {
clear: both;
}
.btn.ctrl {
background-color: #333;
}
input {
font-family: Verdana;
font-size: 2vw;
width: 16vw;
height: 3vw;
margin-bottom: 1vw;
display: block;
text-align: right;
}
<!-- language: lang-html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Calculator</title>
</head>
<body>
</body>
</html>
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论