13. JS с нуля, ваще с нуля функция (возвращаемое значение)

Оценить качество материала и подачу материала автором видео:

Front-end

Трудоустройтесь middle front-end разработчиком на React JS (TypeScript) за 12-16 месяцев обучения с ежедневной менторской поддержкой в формате видео 1 на 1 и коммерческими проектами в портфолио

Перейти на курс
Front-end

Back-end

Трудоустройтесь middle back-end разработчиком за 12-16 месяцев обучения с ежедневной менторской поддержкой в формате видео 1 на 1 и коммерческими проектами в портфолио

Перейти на курс
Back-end

Карьерный бустер

Получите коммерческий опыт на реальных стартапах, прокачайте tech & soft навыки, научитесь работать в команде, проходить собеседования и получите первую работу в IT!

Перейти на курс
Карьерный бустер

Основы Front-end

Сделайте первый шаг в IT, освоив базовые знания разработки и научившись создавать небольшие проекты на JavaScript

Перейти на курс
Основы Front-end

Основы Back-end

Сделайте первый шаг в IT, освоив базовые знания разработки. Без опыта. Без математики. Только практика: JavaScript, SQL, Node JS, база данных

Перейти на курс
Основы Back-end

Возвращаемое значение (return)

Автор конспекта: Бадалова Елена

Продолжаем изучение функций.

Поведение функции

Некоторые функции не возвращают существенное значение после завершения, их основная цель — выполнить какое-либо действие.

То есть, функция выполняет свою логику, но не обязательно выдает внешний результат.

Примеры

  • alert() - выполняет действие (показывает диалоговое окно), но ничего не возвращает.
  • console.log() - выполняет действие (выводит информацию в консоль), но не возвращает значения для дальнейшего присвоения или использования.

Проблема дублирования кода

Для получения значения firstName и lastName приходится писать похожий код.

index.js
var firstNameId = "first-name"
var firstNameEl = document.getElementById(firstNameId)
console.log(firstNameEl.value)
 
var lastNameId = "last-name"
var lastNameEl = document.getElementById(lastNameId)
console.log(lastNameEl.value)

Инкапсуляция логики

Функции могут инкапсулировать сложную или повторяющуюся логику, делая код более читаемым и легким для модификации.

index.js
// Чтобы не было дублирования кода, мы создаем функции и оборачиваем в них весь этот код
 
function firstNameLog() {
  var firstNameId = "first-name"
  var firstNameEl = document.getElementById(firstNameId)
  console.log(firstNameEl.value)
}
 
function lastNameLog() {
  var lastNameId = "last-name"
  var lastNameEl = document.getElementById(lastNameId)
  console.log(lastNameEl.value)
}
 
// теперь мы можем пользоваться нашими функциями (вызывать их), когда нам нужно
 
firstNameLog()
firstNameLog()
firstNameLog()
 
lastNameLog()
lastNameLog()
lastNameLog()

Преимущества инкапсуляции:

  • Уменьшение дублирования кода. Мы пишем логику только один раз, а затем переиспользуем эту функцию, значительно сокращая объем кода.

  • Упрощение модификации и поддержки. Если в будущем потребуется изменить или добавить дополнительное поведение функции, достаточно внести изменения только в одном месте.

  • Универсальность и повторное использование. Создание универсальных функций устраняет необходимость создавать отдельные, почти идентичные функции для каждого элемента, приводя к более эффективному и менее избыточному коду.

Область видимости переменных (Scope)

Глобальная область видимости

Глобальные переменные - это переменные, объявленные вне функций. Они доступны везде.

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

Локальная область видимости

Локальные переменные - это переменные, объявленные внутри блока (функции, цикла, условия), имеют локальную область видимости. Они доступны только в пределах этого блока и его дочерних элементов.

Если попытаться обратиться к такой переменной снаружи функции, она будет undefined.

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

index.js
function lastNameLog() {
  var lastNameId = "last-name"
  var lastNameEl = document.getElementById(lastNameId)
  console.log(lastNameEl.value)
 
  var yo = "I am local" // локальная переменная
 
  console.log(yo)
}
 
var yo = "I am global" // глобальная переменная

Унификация функций

Если функции выполняют похожие действия и отличаются только входными данными (например, ID элемента), эти данные можно передавать как параметры.

index.js
// Вместо двух отдельных функций firstNameLog и lastNameLog, создается одна универсальная функция logValue, которая принимает id как параметр
 
function logValue(id) {
  var el = document.getElementById(id)
  console.log(el.value)
}
 
var firstNameId = "first-name"
var lastNameId = "last-name"
 
// Вызываем функцию с разными параметрами
 
logValue(firstNameId)
logValue(lastNameId)

Возвращаемые значения

Очень часто возникает необходимость, чтобы функция не просто выполняла действие, но и возвращала результат, который может зависеть от переданных параметров.

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

Оператор return

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

Это достигается с помощью оператора return.

При вызове оператора return в функции её выполнение прекращается. Указанное значение возвращается в место вызова функции.

Если возвращаемое значение не указано, вместо него возвращается undefined.

Использование синтаксиса return позволяет функциям быть более универсальными, предоставлять данные для дальнейшей обработки и инкапсулировать логику, что способствует более чистому и модульному коду.

Возвращение значения из функции

index.js
// Создаем функцию, которая принимает ID, находит элемент и возвращает его значение
 
function getValue(id) {
  var el = document.getElementById(id)
  return el.value
}
 
// Использование возвращаемого значения:
 
var value = getValue(firstNameId) // присваиваем значение переменной
 
alert(value) // используем полученное значение напрямую

Оптимизация функции document.getElementById()

index.js
// Создаем функцию, которая инкапсулирует вызов document.getElementById().
 
function getEl(id) {
  console.log("somebody wants find element by id")
  var el = document.getElementById(id)
  return el
}
 
// Теперь вместо document.getElementById('someId') можно писать getEl('someId')
 
var firstNameEl = getEl(firstNameId)
var lastNameEl = getEl(lastNameId)

🏠Домашнее задание

Часть 1

Работа с инпутами, функциями и DOM-манипуляциями

Закрепляем тему функций... Тяжело будет! Держитесь!

Задания

  1. Создать 3 инпута для ввода текста.
  2. Написать JS, который найдёт эти 3 элемента и запишет в них ваше имя (например: «Дима», «Дима», «Дима»).
  3. Ниже написать JS-код, который найдёт эти 3 элемента и запишет в них вашу фамилию.
  4. Как результат выполнения программы мы увидим в 3-х инпутах вашу фамилию.
  5. Потом снова вставить кусок кода из пункта 2 ниже кода из пункта 3 (грех №1 — дублирование кода).

  1. Обернуть код из пункта 2 в функцию setMyNameToAllInputs.
  2. Заменить вызовы кода из пункта 2 на вызовы функции setMyNameToAllInputs.
  3. Инкапсулировать логику из пункта 3 в функцию setMyLastNameToAllInputs, которая найдёт эти инпуты и запишет в них фамилию.
  4. Вызвать функцию setMyLastNameToAllInputs();
  5. Кода стало меньше, теперь мы можем переиспользовать функции многократно.
  6. Видим, что функции очень похожи → дублирование кода.
  7. Сделать функции максимально похожими друг на друга (убрать дублирование внутри).

Пример:

js
var newValue = "Dima"
blabla.value = newValue
blabla2.value = newValue
blabla3.value = newValue

  1. Аналогично переделать функцию для фамилии.
  2. Отличие функций — в значении внутренней переменной:
js
function setMyNameToAllInputs() {
  var newValue = "Dima"
  // здесь меняем значения
}
 
function setMyLastNameToAllInputs() {
  var newValue = "Kuzyuberdin"
  // здесь меняем значения
}
  1. Написать универсальную функцию changeValue(newValue), которая принимает параметр и меняет значения у всех инпутов:
js
function changeValue(newValue) {
  // используем newValue как параметр
  // меняем значения у всех инпутов
}
  1. Закомментировать функции setMyNameToAllInputs и setMyLastNameToAllInputs и использовать универсальную:
js
changeValue("it-kamasutra")
changeValue("google")
changeValue("it-kamasutra")

  1. Добавить в CSS класс error:
css
.error {
  border: 1px solid red;
}
  1. Написать код, который найдёт все 3 инпута с помощью getElementById и каждому добавит класс error.
  2. Видим дублирование кода.
  3. Написать функцию setError(id), которая принимает id, находит элемент и добавляет ему класс error.
  4. Использовать функцию вместо кода из пункта 18.
  5. Добавить в разметку отдельные div и textarea с id, вызвать функцию:
js
setError("div-id")
setError("textarea-id")

Результат: у обоих элементов красная рамка.


  1. Написать функцию getSumm, которая возвращает сумму двух чисел.
js
function getSumm(a, b) {
  return Number(a) + Number(b)
}
  1. Посчитать несколько сумм:
js
var result1 = getSumm(23, 34)
console.log(result1)
 
console.log(getSumm(3434, 12))
console.log(getSumm(23, 23))

  1. Создать функцию sayHello, которая вызывает alert('My name').
  2. Вызвать её через setTimeout:
js
setTimeout(sayHello, 1000)

  1. Попробовать вызвать setError через setTimeout. Нужно создать обёртку:
js
function timeoutError() {
  setError("input-id")
}
 
setTimeout(timeoutError, 1000)

  1. 4 способа спрятать элемент:
  • display: none;
  • visibility: hidden;
  • width: 0px или height: 0px
  • left: -1000000000px (или top)
  • есть и другие (например opacity: 0)
  1. Написать функцию hideElement(id), которая прячет элемент по id.
  2. Написать функцию showElement(id), которая делает противоположное.

  1. setInterval работает как setTimeout, но повторяется каждые N миллисекунд. Сделать счётчик от 0 до бесконечности каждую секунду, выводить результат в инпут.

  1. В функцию getSumm передать строки:
js
getSumm("12", "34")

Без приведения типов результат будет некорректным. Используем Number(a) и Number(b), чтобы получить правильный результат.

Часть 2

Работа с функциями для управления DOM-элементами

Продолжаем вырывать мозг, решая задачи по функциям

Задания

  1. Создать функцию, которая будет удалять элемент по id. Использовать метод .remove().
js
function removeElementById(id) {
  var element = document.getElementById(id)
  if (element != null) {
    element.remove()
  }
}
  1. Создать функцию, которая принимает 2 параметра: id элемента и название css-класса. Функция должна находить элемент и добавлять к нему переданный класс.
js
function addClassToElement(id, className) {
  var element = document.getElementById(id)
  if (element != null) {
    element.classList.add(className)
  }
}
  1. Создать функцию, которая будет прятать элемент по id, уменьшая его width. Используем CSS-свойство transition и дополнительный класс с width: 0px;.
js
function hideElement(id, className) {
  var element = document.getElementById(id)
  if (element != null) {
    setTimeout(function () {
      element.classList.add(className)
    }, 0)
  }
}
  1. Аналогичная функция для показа элемента (убираем класс).
js
function showElement(id, className) {
  var element = document.getElementById(id)
  if (element != null) {
    setTimeout(function () {
      element.classList.remove(className)
    }, 0)
  }
}
  1. Создать функцию, которая принимает массив html-элементов и удаляет те, у которых .value === "2".
js
function removeElementsWithValue2(elements) {
  for (var i = 0; i < elements.length; i++) {
    if (elements[i].value === "2") {
      elements[i].remove()
    }
  }
}
  1. Аналогичная функция, но удаляет элементы с .value === "3".
js
function removeElementsWithValue3(elements) {
  for (var i = 0; i < elements.length; i++) {
    if (elements[i].value === "3") {
      elements[i].remove()
    }
  }
}
  1. Избавляемся от дублирования: пишем универсальную функцию, которая принимает значение valueForDelete.
js
function removeElementsWithValue(elements, valueForDelete) {
  for (var i = 0; i < elements.length; i++) {
    if (elements[i].value === valueForDelete) {
      elements[i].remove()
    }
  }
}
  1. Создать функцию, которая принимает массив элементов и удаляет те, у которых есть css-класс "hey".
js
function removeElementsWithClassHey(elements) {
  for (var i = 0; i < elements.length; i++) {
    if (elements[i].classList.contains("hey")) {
      elements[i].remove()
    }
  }
}
  1. Создать функцию hasClassHey, которая принимает элемент и возвращает true, если у него есть класс "hey", иначе — false.
js
function hasClassHey(element) {
  return element.classList.contains("hey")
}
  1. Создать функцию removeElementIfConditionsTrue, которая принимает массив элементов и функцию-предикат. Если предикат вернёт true, элемент удаляется.
js
function removeElementIfConditionsTrue(elements, predicate) {
  for (var i = 0; i < elements.length; i++) {
    if (predicate(elements[i])) {
      elements[i].remove()
    }
  }
}

Пример использования:

js
removeElementIfConditionsTrue(document.querySelectorAll("input"), hasClassHey)
removeElementIfConditionsTrue(document.querySelectorAll("input"), function (el) {
  return el.value === "2"
})

Часть 3

Небольшие пояснения

Ну и финальный закреп нелёгкой темы ФУНКЦИИ

  1. Функция должна работать только с теми переменными, которые у неё объявлены внутри. Если функции нужно что-либо из “внешнего” мира, то это что-то должно передаваться во время вызова функции как параметр.

Плохой вариант (используем внешнюю переменную)

html
<script>
  var number = 5
 
  function multiplyByTen() {
    // функция обращается к глобальной переменной number — работает, но это плохо
    var result = number * 10
    alert(result)
  }
 
  multiplyByTen()
</script>

Переделаем так (передаём значение параметром):

html
<script>
  function multiplyByTen(n) {
    var result = n * 10
    alert(result)
  }
 
  var number = 5
  // теперь функция не “лезет” во внешний мир — получает данные параметром
  multiplyByTen(number)
</script>

Задания

Мы запускаем какую-то функцию doSommething и хотим, чтобы она в конце, когда отработает, сообщила нам о своём завершении, вызвав нашу другую callback-функцию. Где функция doSommething возьмёт такую callback? Мы передадим её параметром.

1) Функция getArraySum + колбэки alertResult и logResult

html
<script>
  function getArraySum(numbers, callback) {
    var sum = 0
    for (var i = 0; i < numbers.length; i++) {
      sum = sum + numbers[i]
    }
    // не возвращаем через return — а вызываем переданный колбэк
    callback(sum)
  }
 
  function alertResult(n) {
    alert(n)
  }
 
  function logResult(n) {
    console.log(n)
  }
 
  // вызовы
  getArraySum([123, 23, 43, 34], alertResult)
  getArraySum([123, 23, 43, 34], logResult)
</script>

2) Процессоры элементов: deleter, hider, выбор функции по имени

html
<script>
  // удаляльщик, должен удалить переданный ей элемент
  function deleter(element) {
    if (element) element.remove()
  }
 
  // прятальщик, должен спрятать переданный ей элемент
  function hider(element) {
    if (element) {
      element.style.display = "none"
    }
  }
 
  // возвращает конкретную функцию по имени
  function getConcreteFunctionByName(name) {
    switch (name) {
      case "d":
      case "deleter":
        return deleter
      case "h":
      case "hider":
        return hider
      default:
        return function noop() {}
    }
  }
 
  // пример применения к массиву элементов
  var elements = document.getElementsByClassName("some-class")
  var concretteProccessor = getConcreteFunctionByName("deleter")
 
  for (var i = 0; i < elements.length; i++) {
    var element = elements[i]
    concretteProccessor(element)
  }
</script>

3) Применить набор процессоров к каждому элементу: proccessElements

html
<script>
  function addErrorClass(element) {
    if (element) element.classList.add("error")
  }
 
  function add100PercentWidthClass(element) {
    if (element) element.classList.add("w-100")
  }
 
  function clearValue(element) {
    if (element && "value" in element) element.value = ""
  }
 
  // processors — массив функций (процессоров)
  function proccessElements(elements, processors) {
    for (var i = 0; i < elements.length; i++) {
      var el = elements[i]
      for (var j = 0; j < processors.length; j++) {
        var proc = processors[j]
        proc(el)
      }
    }
  }
 
  // пример использования
  var elements = document.getElementsByClassName("some-class")
  proccessElements(elements, [addErrorClass, add100PercentWidthClass, clearValue])
</script>

Боевой маршрут (JS Ваще с нуля)

Видеоурок - 14 видео из 29