vscode로 코드를 작성하고 chrome 웹 브라우저와 개발자도구를 이용해 코드를 실행해보았다.
아주 간단한 계산만 할 수 있는 계산기를 만들어보았다. 피연산자 2개에 대해 + - * / 계산이 가능하다.
어떤 이벤트가 일어났을 때 각 이벤트에 따라 상황을 판단을 하고 함수를 실행하도록 순서도를 짜야한다.
코드를 짤 때는 반복되는 부분을 제거해서 더 효율적인 코드를 짤 수 있다. 계산기의 숫자 버튼을 누르면 해당 숫자가 찍히도록 하는 코드를 생각해보면
document.querySelector('#num-0').addEventListener('click', () => {
if (operator) {
numTwo += '0';
} else {
numOne += '0';
}
$result.value += '0'; //화면에 값을 표시해주는 코드
};
이런 식으로 0부터 9까지 다 적어줘야한다. 하지만 같은 코드에서 어떤 숫자가 찍히는지만 달라지기 때문에 이 값을 매개변수에 넣어주면 된다.
const onClickNumber = (number) => {
if (operator) {
numTwo += number;
} else {
numOne += number;
}
$result.value += number;
};
document.querySelector('#num-0').addEventListener('click', onClickNumber(0));
이렇게만 해도 중복이 많이 제거가 된다. 여기서 한 가지 주의할 점은 이벤트리스너의 리스너 함수 자리에 onClickNumber()를 넣으면 함수가 아닌 이 함수의 반환값이 들어오게 된다. 즉 현재 콜백함수의 반환값이 없으므로 undefined를 반환하는데 이는 'click' 이벤트가 발생하면 undefined를 실행하라는 뜻이므로 에러를 발생시킨다. 이를 방지하려면
const onClickNumber = (number) => {
if (operator) {
numTwo += number;
} else {
numOne += number;
}
$result.value += number;
return () => {
};
};
document.querySelector('#num-0').addEventListener('click', onClickNumber(0));
콜백함수가 함수를 반환하게끔 만들어주면 된다. 하지만 일단 자리만 맞춰준 것 뿐이지 이대로 실행을하면 return하는 함수 안에 아무 실행도 들어있지 않기 때문에 아무 일도 일어나지 않는다.
const onClickNumber = (number) => {
return () => {
if (operator) {
numTwo += number;
} else {
numOne += number;
}
$result.value += number;
};
};
document.querySelector('#num-0').addEventListener('click', onClickNumber(0));
이렇게 return안에 함수의 동작까지 넣어줘야 제대로 작동이 된다.
또 중괄호와 return이 붙으면 생략이 가능해진다.
const onClickNumber = (number) => () => {
if (operator) {
numTwo += number;
} else {
numOne += number;
}
$result.value += number;
};
};
document.querySelector('#num-0').addEventListener('click', onClickNumber(0));
화살표가 연달아나오는 함수가 함수를 리턴하는 모양으로 줄일 수 있다. 이렇게 함수를 매개변수로 사용하거나 함수를 반환하는 함수를 고차함수라고 한다.
위에서 고차함수를 이용해서 줄인 함수에서 두번째 함수의 매개변수로 event객체가 들어간다. 이 event객체는 브라우저가 넣어주는 것이다.
const onClickNumber = (number) => (event) => {
if (operator) {
numTwo += number;
} else {
numOne += number;
}
$result.value += number;
};
};
document.querySelector('#num-0').addEventListener('click', onClickNumber(0));
event객체를 받기 때문에 고차함수를 쓰지 않고 event.target.textContent를 이용해서 입력할 수도 있다.
const onClickNumber = (event) => {
if (operator) {
numTwo += event.target.textContent;
} else {
numOne += event.target.textContent;
}
$result.value += event.target.textContent;
};
};
document.querySelector('#num-0').addEventListener('click', onClickNumber);
즉 내가 event객체를 가져오면 클릭한 버튼의 글자를 그대로 가져올 수 있다.
계산기에서 c버튼을 누르면 입력한 값이 지워질 뿐 만 아니라 입력받았던 변수들(numOne, numTwo, operator)도 초기화되어야한다.
document.querySelector('#clear').addEventListener('click', () => {
numOne = '';
operator = '';
numTwo = '';
$operator.value = '';
$result.value = ''; //이건 화면만 초기화
clear클래스의 버튼을 누르면 안에 내용들이 실행되는 것이다. 이전 과제인 끝말잇기에서는 화면만 초기화하면 되었다. 하지만 이번에는 변수들에 저장된 값도 다 빈 문자열 ''로 초기화 해주어야한다는 것을 기억해야한다.
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<title>계산기</title>
<style>
* { box-sizing: border-box }
#result { width: 180px; height: 50px; text-align: right }
#operator { width: 50px; height: 50px; margin: 5px; text-align: center }
button { width: 50px; height: 50px; margin: 5px}
</style>
</head>
<!--계산기 버튼 구현-->
<body>
<input readonly id="operator">
<input readonly type="number" id="result">
<div class="row">
<button id="num-7">7</button>
<button id="num-8">7</button>
<button id="num-9">7</button>
<button id="plus">+</button>
</div>
<div class="row">
<button id="num-4">4</button>
<button id="num-5">5</button>
<button id="num-6">6</button>
<button id="minus">-</button>
</div>
<div class="row">
<button id="num-1">1</button>
<button id="num-2">2</button>
<button id="num-3">3</button>
<button id="divide">/</button>
</div>
<div class="row">
<button id="clear">C</button>
<button id="num-0">0</button>
<button id="calculate">=</button>
<button id="multiply">x</button>
</div>
<script>
let numOne = '';
let operator = '';
let numTwo = '';
const $operator = document.querySelector('#operator');
const $result = document.querySelector('#result');
const onClickNumber = (event) => {
if (!operator) {
numOne += event.target.textContent;
$result.value += event.target.textContent;
return;
}
if (!numTwo) {
$result.value = '';
}
numTwo += event.target.textContent;
$result.value += event.target.textContent;
}; //중첩 if문 제거 후 코드
document.querySelector('#num-0').addEventListener('click', onClickNumber);
document.querySelector('#num-1').addEventListener('click', onClickNumber);
document.querySelector('#num-2').addEventListener('click', onClickNumber);
document.querySelector('#num-3').addEventListener('click', onClickNumber);
document.querySelector('#num-4').addEventListener('click', onClickNumber);
document.querySelector('#num-5').addEventListener('click', onClickNumber);
document.querySelector('#num-6').addEventListener('click', onClickNumber);
document.querySelector('#num-7').addEventListener('click', onClickNumber);
document.querySelector('#num-8').addEventListener('click', onClickNumber);
document.querySelector('#num-9').addEventListener('click', onClickNumber);
//이벤트 객체를 이용해 코드 중복을 제거했다.
const onClickOperator = (op) => () => {
if (numOne) {
operator = op;
$operator.value = op;
} else {
alert('숫자를 먼저 입력하세요');
}
}
document.querySelector('#plus').addEventListener('click', onClickOperator('+'));
document.querySelector('#minus').addEventListener('click', onClickOperator('-'));
document.querySelector('#divide').addEventListener('click', onClickOperator('/'));
document.querySelector('#multiply').addEventListener('click', onClickOperator('*'));
document.querySelector('#calculate').addEventListener('click', () => {
if (numTwo) {
switch (operator) {
case '+':
$result.value = parseInt(numOne) + parseInt(numTwo);
break;
case '-':
$result.value = numOne - numTwo;
break;
case '*':
$result.value = numOne * numTwo;
break;
case '/':
$result.value = numOne / numTwo;
break;
default:
break;
}
} else {
alert('숫자를 먼저 입력하세요.');
}
});
document.querySelector('#clear').addEventListener('click', () => {
numOne = '';
operator = '';
numTwo = '';
$operator.value = '';
$result.value = ''; //이건 화면만 초기화
//초기상태가 뭔지 알기 위해서는 초기상태 (let으로 선언한 변수)들을 한군데 몰아넣는게 좋다.
});
</script>
</body>
</html>
현재 내가 만든 계산기는 1번만 연산이 가능하다. 피연산자가 3개 이상인 연산은 하지 못한다. 이 연산을 가능하도록 만들어보는 것이 추가 과제이다. 아직 구현해보지 못했지만
=을 눌렀을 때 값을 어떤 변수에 저장하고, 이 변수에 값이 들어있으면 숫자가 아닌 연산자를 바로 받아서 이 변수의 값과 연산하도록 만들면 될 것같다.