Как оптимизировать код JS

184
10 июня 2022, 11:30

сделал простой калькулятор, который складывает, вычитает, умножает и делит. Но как этот код уменьшить? На сколько я понимаю это делается с помощью функций, но я не понимаю как это реализовать.

function plus() {
      
      var num1, num2, result;
      num1 = document.getElementById('n1').value;
      num1 = parseInt(num1);
    
      num2 = document.getElementById('n2').value;
      num2 = Number(num2);
    
      result = num1 + num2;
      document.getElementById('out').innerHTML = result;
    }
    
    function minus() {
      var num1, num2, result;
      num1 = document.getElementById('n1').value;
      num1 = parseInt(num1);
    
      num2 = document.getElementById('n2').value;
      num2 = Number(num2);
    
      result = num1 - num2;
      document.getElementById('out').innerHTML = result;
    }
    
    function multiply() {
      var num1, num2, result;
      num1 = document.getElementById('n1').value;
      num1 = parseInt(num1);
    
      num2 = document.getElementById('n2').value;
      num2 = Number(num2);
    
      result = num1 * num2;
      document.getElementById('out').innerHTML = result;
    
    }
    
    function share() {
      var num1, num2, result;
      num1 = document.getElementById('n1').value;
      num1 = parseInt(num1);
    
      num2 = document.getElementById('n2').value;
      num2 = Number(num2);
    
      result = num1 / num2;
      document.getElementById('out').innerHTML = result;
    } 
        <h1>калкулятор</h1>
        <div class="NumAll">
            <p class="Num1" >
                
                <input type="text" id="n1">
                
            </p>
            <p class="Num2" >
                <input type="text" id="n2">
            </p>
        </div>
    
        <div class="btn">
    
            <button onclick="minus()" >Вычесть</button>
    
            <button onclick="plus()" >Сложить</button>
    
            <button onclick="multiply()" >Умножить</button>
    
            <button onclick="share()" >Делить</button>
    
        </div>
        
        <p class="Res" id="out">Результат</p>

Answer 1

Все четыре функции у вас отличаются лишь операцией. Логично будет сделать функцию, которая получает два числа, тип операции и возвращает результат. А получение и вывод данных прописать лишь в одном месте.

document.querySelector(селектор) возвращает первый элемент на странице, который будет соответствовать CSS-селектору. Если и его не хочется дублировать везде, можно сделать коротенькую функцию-обертку и для него.

Вынес onclick из HTML, потому что оно не используется дальше учебных примеров ( https://google.com#q=dont+use+inline+onclick ). Хорошо бы сразу привыкнуть к addEventListener — добавлять обработчики отдельно от HTML.

let inp_1 = first("#n1"), inp_2 = first("#n2"), out = first("#out");
first(".btn").addEventListener("click", function(e) {
  let operation = e.target.dataset.oper;  
  if (!operation) return; // Операция не указана? Значит кликнули не на кнопку.
  
  let a = +inp_1.value; // унарный плюс делает то же, что и Number()
  let b = +inp_2.value;
  
  out.textContent = calc(a, operation, b);
});
/***/
function calc(a, oper, b) {  
  switch (oper) {
    case "sub": return a - b;
    case "sum": return a + b;
    case "mul": return a * b;
    case "div": return a / b;
  }
}
function first(selector) {
  return document.querySelector(selector);
}
<h1>калкулятор</h1>
<div class="NumAll">
  <p class="Num1">
    <input type="text" id="n1">
  </p>
  <p class="Num2">
    <input type="text" id="n2">
  </p>
</div>
<div class="btn">
  <button data-oper="sub">Вычесть</button>
  <button data-oper="sum">Сложить</button>
  <button data-oper="mul">Умножить</button>
  <button data-oper="div">Делить</button>
</div>
<p class="Res" id="out">Результат</p>

Answer 2

Можно определить класс с множеством допустимых операторов.

class Calc {
  binary_operators = {
    '+': (a, b) => a + b,
    '-': (a, b) => a - b,
    '*': (a, b) => a * b,
    '/': (a, b) => a / b,
  };
  unary_operators = {
    'sin': (a) => Math.sin(a),
  };
  constructor(aEl, bEl, resEl) {
    this.aEl = aEl;
    this.bEl = bEl;
    this.resEl = resEl;
  }
  initButtons(buttons) {
    buttons.forEach(btn => btn.addEventListener('click', () => {
      if (btn.dataset.binOperator) {
        this.binaryOp(btn.dataset.binOperator);
      } else if (btn.dataset.unaryOperator) {
        this.unaryOp(btn.dataset.unaryOperator);
      }
    }));
  }
  unaryOp(op) {
    const operator = this.unary_operators[op];
    if (!operator) throw new Error(`Unknown operator ${op}`);
    this.resEl.innerText = operator(+this.aEl.value);
  }
  binaryOp(op) {
    const operator = this.binary_operators[op];
    if (!operator) throw new Error(`Unknown operator ${op}`);
    this.resEl.innerText = operator(+this.aEl.value, +this.bEl.value);
  }
}
const calc = new Calc(
  document.getElementById('n1'),
  document.getElementById('n2'),
  document.getElementById('out')
);
calc.initButtons(document.querySelectorAll('.calc-button'));
<h1>Калькулятор</h1>
<div class="NumAll">
  <p class="Num1">
    <input type="text" id="n1">
  </p>
  <p class="Num2">
    <input type="text" id="n2">
  </p>
</div>
<div class="btn">
  <button data-bin-operator="-" class="calc-button">Вычесть</button>
  <button data-bin-operator="+" class="calc-button">Сложить</button>
  <button data-bin-operator="*" class="calc-button">Умножить</button>
  <button data-bin-operator="/" class="calc-button">Делить</button>
  <button data-unary-operator="sin" class="calc-button">sin(x)</button>
</div>
<p class="Res" id="out">Результат</p>

А это кое-кому похоже нечем было заняться перед концом рабочей недели)

class Calc {
  operations = {
    'id': (a, b) => b,
     '+': (a, b) => a + b,
     '-': (a, b) => a - b,
     '*': (a, b) => a * b,
     '/': (a, b) => a / b,
  };
  unary_operators = {
    'sin': (a) => Math.sin(a),
  };
  constructor(el) {
    this.el = el;
    this.resEl = this.el.querySelector('.calc-result');
    this.initialize();
    this.reset();
  }
  reset() {
    this.resEl.innerText = '0';
    this.operandA = 0;
    this.operandB = 0;
    this.newInputFlag = true;
    this.currentOperator = this.operations['id'];
  }
  #calc() {
    const result = this.currentOperator(this.operandA, this.operandB);
    this.operandA = result;
    this.resEl.innerText = parseFloat(result.toFixed(16));
    this.newInputFlag = true;
  }
  #addDigit(digit) {
    if (this.newInputFlag) {
      this.resEl.innerText = '';
    }
    this.resEl.innerText = (this.resEl.innerText + digit).replace(/^0+(\d)/, '$1');
    this.newInputFlag = false;
  }
  #addPoint() {
    if (this.newInputFlag) {
      this.resEl.innerText = '0';
    }
    if (-1 === this.resEl.innerText.indexOf('.')) {
      this.resEl.innerText = this.resEl.innerText + '.';
      this.newInputFlag = false;
    }
  }
  #getResult() {
    try {
      if (!this.newInputFlag) {
        this.operandB = Number(this.resEl.innerText);
      }
      this.#calc();
    } catch (e) {
      this.resEl.innerText = 'ЕГГОГ';
    }
  }
  #setOperator(operator) {
    if (!this.newInputFlag) {
      this.operandB = Number(this.resEl.innerText);
      this.#calc();
    }
    if (!operator) throw new Error(`Unknown operation ${op}`);
    this.currentOperator = operator;
  }
  #initButton(btn) {
    btn.addEventListener('click', () => {
      switch (btn.dataset.calcType) {
        case 'reset':    this.reset(); break;
        case 'digit':    this.#addDigit(btn.dataset.digit); break;
        case 'point':    this.#addPoint(); break;
        case 'result':   this.#getResult(); break;
        case 'operator': this.#setOperator(this.operations[btn.dataset.calcOp]); break;
      }
    });
  }
  initialize() {
    this.el.querySelectorAll('.calc-button')
      .forEach(btn => this.#initButton(btn));
  }
}
const calc = new Calc(document.querySelector('.calc'));
.calc {
  border: 3px solid silver;
  padding: 1em;
  width: 360px;
  height: 360px;
  margin: 0 auto;
  position: relative;
  background: #cec;
}
.calc-result {
  border: 1px solid silver;
  padding: 0.2em;
  margin: 0.5em;
  background: #aaa;
  text-align: right;
  line-height: 2em;
  font-size: 2em;
  white-space: nowrap;
}
table.calc-buttons {
  width: 100%;
  height: 70%;
}
table.calc-buttons td .calc-button {
  width: 100%;
  height: 100%;
}
<div class="calc">
  <div class="calc-result">0</div>
    <table class="calc-buttons">
      <tr>
        <td>
          <button class="calc-button" data-calc-type="reset">C</button>
        </td>
        <td>
          <button class="calc-button" data-calc-type="operator" data-calc-op="/">/</button>
        </td>
        <td>
          <button class="calc-button" data-calc-type="operator" data-calc-op="*">*</button>
        </td>
        <td>
          <button class="calc-button" data-calc-type="operator" data-calc-op="-">-</button>
        </td>
      </tr>
      <tr>
        <td>
          <button class="calc-button" data-calc-type="digit" data-digit="7">7</button>
        </td>
        <td>
          <button class="calc-button" data-calc-type="digit" data-digit="8">8</button>
        </td>
        <td>
          <button class="calc-button" data-calc-type="digit" data-digit="9">9</button>
        </td>
        <td rowspan="2">
          <button class="calc-button" data-calc-type="operator" data-calc-op="+">+</button>
        </td>
      </tr>
      <tr>
        <td>
          <button class="calc-button" data-calc-type="digit" data-digit="4">4</button>
        </td>
        <td>
          <button class="calc-button" data-calc-type="digit" data-digit="5">5</button>
        </td>
        <td>
          <button class="calc-button" data-calc-type="digit" data-digit="6">6</button>
        </td>
      </tr>
      <tr>
        <td>
          <button class="calc-button" data-calc-type="digit" data-digit="1">1</button>
        </td>
        <td>
          <button class="calc-button" data-calc-type="digit" data-digit="2">2</button>
        </td>
        <td>
          <button class="calc-button" data-calc-type="digit" data-digit="3">3</button>
        </td>
        <td rowspan="2">
          <button class="calc-button" data-calc-type="result">=</button>
        </td>
      </tr>
      <tr>
        <td colspan="2">
          <button class="calc-button" data-calc-type="digit" data-digit="0">0</button>
        </td>
        <td>
          <button class="calc-button" data-calc-type="point">.</button>
        </td>
      </tr>
    </table>
</div>

Answer 3

P.S. eval() лучше не использовать с связи с безопасностью, но в нашем случаи +document...value возвратит либо число, либо NaN.

UPD Вместо eval можно использовать синтаксис Function(...).

const out = document.getElementById('out');
function calc(operation) {
  out.innerHTML = Function(`return (${+document.getElementById('n1').value} ${operation} ${+document.getElementById('n2').value})`)()
}
<h1>калкулятор</h1>
<div class="NumAll">
  <p class="Num1">
    <input type="text" id="n1">
  </p>
  <p class="Num2">
    <input type="text" id="n2">
  </p>
</div>
<div class="btn">
  <button onclick="calc('-')">Вычесть</button>
  <button onclick="calc('+')">Сложить</button>
  <button onclick="calc('*')">Умножить</button>
  <button onclick="calc('/')">Делить</button>
</div>
<p class="Res" id="out">Результат</p>

READ ALSO
JS задача с циклом

JS задача с циклом

Нужно написать цикл, который перебирает числа от 4 до 37 и выполняет следующие действия:

208
Склонение слов по русским падежам

Склонение слов по русским падежам

Подскажите, почему неверно склоняются слова

202
Как подключить SwiperJS по npm на NuxtJS?

Как подключить SwiperJS по npm на NuxtJS?

Как подключить SwiperJS по npm на NuxtJS?

237
Анимировать выделение шрифта на кривой SVG

Анимировать выделение шрифта на кривой SVG

У меня есть текст, который движется по кругу SVG, и который масштабируется в зависимости от размера окна - Я хочу анимировать текст так, чтобы...

184