Skip to content

Refactoring Part 2

Unser EventListener sieht im Moment so aus:

calculatorButtonsDiv.addEventListener('click', e => {
// ...
// Release operator pressed state
const operatorKeys = [...calculatorButtonsDiv.children].filter(
button => button.dataset.buttonType === 'operator';
)
operatorKeys.forEach(button => button.classList.remove('is-pressed'));
if (buttonType === 'clear') {
// ...
}
if (buttonType !== 'clear') {
// ...
}
if (buttonType === 'number') {
// ...
}
if (buttonType === 'decimal') {
// ...
}
if (buttonType === 'operator') {
// ...
}
if (buttonType === 'equal') {
// ...
}
calculator.dataset.previousButtonType = buttonType;
});

Wir können den EventListener-Code vereinfachen, indem wir jeden “Button”-Teil in eine zugeordnete (dedizierte) Funktion umbrechen:

calculatorButtonsDiv.addEventListener('click', event => {
// ...
if (buttonType !== 'clear') {
// ...
}
if (buttonType === 'clear') handleClearkey();
if (buttonType === 'number') handleNumberkeys();
if (buttonType === 'decimal') handleDecimalkey();
if (buttonType === 'operator') handleOperatorkeys();
if (buttonType === 'equal') handleEqualkey();
calculator.dataset.previousButtonType = buttonType;
});

Wenn wir viele if-Statements haben, wollen wir idealerweise early returns verwenden. Ein early return sagt uns, dass der restliche Code nicht mehr ausgeführt wird. Das macht Code einfacher verständlich.

// Ideal, but doesn't work:
calculatorButtonsDiv.addEventListener('click', event => {
// ...
if (buttonType !== 'clear') {
// ...
}
if (buttonType === 'clear') return handleClearkey();
if (buttonType === 'number') return handleNumberkeys();
if (buttonType === 'decimal') return handleDecimalkey();
if (buttonType === 'operator') return handleOperatorkeys();
if (buttonType === 'equal') return handleEqualkey();
calculator.dataset.previousButtonType = buttonType;
});

Das funktioniert nicht, weil wir am Ende jedes EventListeners buttonType dem previousButtonType zuordnen müssen.

In diesen Fällen ist ein switch-Statement perfekt. Mit switch erhalten wir die benötigte Klarheit und können immer noch die letzte Codezeile ausführen:

calculatorButtonsDiv.addEventListener('click', event => {
// ...
if (buttonType !== 'clear') {
// ...
}
switch (buttonType) {
case 'clear':
handleClearKey();
break;
case 'number':
handleNumberKeys();
break;
case 'decimal':
handleDecimalKey();
break;
case 'operator':
handleOperatorKeys();
break;
case 'equal':
handleEqualKey();
break;
}
calculator.dataset.previousButtonType = buttonType;
});

Wir können den Code noch knapper schreiben, in dem wir den case, die aufgerufene Funktion und das break in eine Zeile bringen:

switch (buttonType) {
case 'clear': handleClearKey(calculator, button); break;
case 'number': handleNumberKeys(calculator, button); break;
case 'decimal': handleDecimalKey(calculator); break;
case 'operator': handleOperatorKeys(calculator, button); break;
case 'equal': handleEqualKey(calculator); break;
}

Wir erzeugen diese Funktion, indem wir alles aus dem if (buttonType === 'clear') kopieren.

function handleClearKey() {
display.textContent = '0';
button.textContent = 'AC';
if (previousButtonType === 'clear') {
delete calculator.dataset.firstValue;
delete calculator.dataset.operator;
delete calculator.dataset.modifierValue;
}
}

Um die Funktion aufzurufen, benötigen wir calculator und button. Wir können diese beiden Variablen der Funktion übergeben:

function handleClearKey(calculator, button) {
const { previousButtonType } = calculator.dataset;
if (buttonType === 'clear') {
display.textContent = '0';
button.textContent = 'AC';
if (previousButtonType === 'clear') {
delete calculator.dataset.firstValue;
delete calculator.dataset.operator;
delete calculator.dataset.modifierValue;
}
}
}
calculatorButtonsDiv.addEventListener('click', event => {
// ...
switch (buttonType) {
case 'clear':
handleClearKey(calculator, button);
break;
}
// ...
})

Wir erzeugen eine Funktion handleNumberKeys. Dazu kopieren wir wieder alles von `if (buttonType === ‘number’) in diese Funktion.

function handleNumberKeys() {
if (displayValue === '0') {
display.textContent = key;
} else {
display.textContent = displayValue + key;
}
if (previousButtonType === 'operator') {
display.textContent = key;
}
if (previousButtonType === 'equal') {
resetCalculator();
display.textContent = key;
}
}

Für diese Funktion benötigen wir key, previousButtonType und displayValue. key bekommen wir aus button.dataset und previousButtonType aus calculator. Wir übergeben also diese beiden Variablen.

function handleNumberKeys(calculator, button) {
const { previousButtonType } = calculator.dataset;
const { key } = button.dataset;
// ...
}

displayValue erhalten wir aus getDisplayValue:

function handleNumberKeys(calculator, button) {
const displayValue = getDisplayValue();
// ...
}

Verwendung von handleNumberKeys:

calculatorButtonsDiv.addEventListener('click', event => {
// ...
switch (buttonType) {
// ...
case 'number':
handleNumberKeys(calculator, button);
break;
}
// ...
})

Wieder beginnen wir damit, alles aus if (buttonType === 'decimal') in eine neue Funktion handleDecimalKey zu kopieren:

function handleDecimalKey() {
if (!displayValue.includes('.')) {
display.textContent = displayValue + '.';
}
if (previousButtonType === 'equal') {
resetCalculator();
display.textContent = '0';
}
if (previousButtonType === 'operator') {
display.textContent = '0.';
}
}

handleDecimalKey benötigt displayValue und previousButtonType.

  • displayValue erhalten wir aus getDisplayValue
  • previousButtonType finden wir in calculator.dataset

Wir müssen also calculator an die Funktion handleDecimalKey übergeben.

function handleDecimalKey(calculator) {
const displayValue = getDisplayValue();
const { previousButtonType } = calculator.dataset;
if (!displayValue.includes('.')) {
display.textContent = displayValue + '.';
}
if (previousButtonType === 'equal') {
resetCalculator();
display.textContent = '0.';
}
if (previousButtonType === 'operator') {
display.textContent = '0.';
}
}

Verwendung von handleDecimalKey:

calculatorButtonsDiv.addEventListener('click', event => {
// ...
switch (buttonType) {
// ...
case 'decimal':
handleDecimalKey(calculator);
break;
}
// ...
});

Wir kopieren alles aus if (buttonType === 'operator') in eine neue Funktion handleOperatorKeys.

function handleOperatorKeys() {
button.classList.add('is-pressed');
const firstValue = calculator.dataset.firstValue;
const operator = calculator.dataset.operator;
const secondValue = displayValue;
if (
previousButtonType !== 'operator' &&
previousButtonType !== 'equal' &&
firstValue &&
operator
) {
const result = calculate(firstValue, operator, secondValue) {
display.textContent = result;
calculator.dataset.firstValue = result;
} else {
calculator.dataset.firstValue = displayValue;
}
calculator.dataset.operator = button.dataset.key
}
}

In dieser Funktion benötigen wir calculator, button und displayValue. Wir übergeben calculator und button an die Funktion.

function handleOperatorKeys(calculator, button) {
const { previousButtonType, firstValue, operator } = calculator.dataset;
// ...
}

displayValue erhalten wir aus getDisplayValue:

function handleOperatorKeys(calculator, button) {
const displayValue = getDisplayValue();
// ...
}

Verwendung von handleOperatorKeys:

calculatorButtonsDiv.addEventListener('click', event => {
// ...
switch (buttonType) {
// ...
case 'operator':
handleOperatorKeys(calculator, button);
break;
}
});

Zuletzt benötigen wir noch eine Funktion für den equal key. Wir kopieren also wieder alles aus if (buttonType === 'equal') in die neue Funktion handleEqualKey.

function handleEqualKey() {
const firstValue = calculator.dataset.firstValue;
const operator = calculator.dataset.operator;
const modifierValue = calculator.dataset.modifierValue;
const secondValue = modifierValue || displayValue;
if (firstValue && operator) {
const result = calculate(firstValue, operator, secondValue);
display.textContent = result;
calculator.dataset.firstValue = result;
calculator.dataset.modifierValue = secondValue;
} else {
display.textContent = parseFloat(displayValue) * 1;
}
}

In dieser Funktion benötigen wir calculator und displayValue:

function handleEqualKey(calculator) {
const displayValue = getDisplayValue();
const { firstValue, operator, modifierValue } = calculator.dataset;
// ...
}

Verwendung von handleEqualKey:

calculatorButtonsDiv.addEventListener('click', event => {
// ...
switch (buttonType) {
// ...
case 'equal':
handleEqualKey(calculator, button);
break;
}
});