skip to main content

function()

js

Кратко 🔗

Функция — это блок из различных команд. Функции служат для упорядочивания и структурирования кода программы.

Примеры простейших встроенных функций — это alert(message) и prompt(message, default), но можно создавать и свои 🤘🏼

Как пишется 🔗

Базовый вариант создания функции:

function hello(name) {
alert("hello" + name)
}

Как это понять 🔗

Вначале идет ключевое слово function, после него имя функции, затем список параметров в скобках (в примере выше параметр один, но может быть и больше) и тело функции — код, который выполняется при её вызове.

💡 Имя функции 🔗

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

var factorial = function fac(n) {
return n < 2 ? 1 : n * fac(n - 1)
}

console.log(factorial(3))

Это пример рекурсивной функции.

Рекурсией называется такая конструкция, при которой функция вызывает саму себя. Достаточно часто такое применяется в математических операциях. Но не ограничивается ими.

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

Переменные внутри функции существуют только внутри этой функции.

const sum = 100
function sumOne() {
const sum = 50
return sum
}
function sumTwo() {
const sum = 75
return sum
}
alert(sum) // 100
alert(sumOne()) // 50
alert(sum) // 100
alert(sumTwo()) // 75
alert(sum) // 100

Что это значит? Каждый раз, когда происходит выполнение функции, список переменных доступных для использования меняется. Хотя переменная и называется sum, в каждой функции эта переменная сама по себе и поэтому изменение переменной в функции sumOne не влияет на глобальную sum и не влияет на локальные (в других функциях) переменные с именем sum.

Почему так происходит? Из-за контекста выполнения функции 😎

💡 Контекст функции 🔗

У кода в момент выполнения его интерпретатором есть «окружение». Это функция которая сейчас отрабатывает, переменные содержащиеся в ней, глобальные переменные. Это все и есть контекст.

Существует 3 типа кода:

  • код функции — код, выполняющийся в теле функции;
  • глобальный код — код, не выполняющийся в рамках какой-либо функции.
  • eval-код — код, выполняющийся внутри функции eval(); Использование функции крайне редко.

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

💡 Параметры 🔗

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

Например, этот код выводит сообщения:

function showMessage(user, message) {
alert(user + ": " + message)
}
showMessage("Маша", "Привет!")
showMessage("Петя", "Привет, Маша, познакомимся?")

Разберем код:

Название функции showMessage

Она принимает 2 параметра под названиями user и message. Когда описываешь свою функцию — можно называть эти параметры по своему.

Внутри showMessage идёт вызов встроенной функции alert.

Вызов alert происходит со строкой, состоящей из трех частей:

  • переменная user
  • текст: двоеточие и пробел
  • переменная message

🤖 Иногда внутри функции требуется поменять значение параметра. Например, добавить форматирование. Пока параметр является примитивом — с этим нет проблем, потому что примитив копируется по значению.

Но в случае не примитивов (например объектов или массивов) происходит копирование по ссылке. Изменяя параметр переданный по ссылке — он изменяется не только в функции, но и извне.

See the Pen function() как понять параметры by vindi-r (@vindi-r) on CodePen.

💡 В примерах выше было слово return. Что это такое и для чего нужно — более подробно раскрыто в отдельной статье про return 😎

💡 Именованные и анонимные функции 🔗

В JavaScript есть несколько типов функций, которые несколько отличаются друг от друга. Первый тип — именованные.

function namedFunction() {}
// Это именованная функция, потому что у неё есть имя.

Противоположность именованным функциям — анонимные. У таких имени нет:

function() {};
// Функция без имени — анонимная.

Они работают одинаково, но по-разному ведут себя в консоли и стеке вызовов. Допустим, мы написали программу, в которой есть ошибка. Если наши функции были именованными, то стек вызовов, покажет, какая функция вызвала какую и что привело к ошибке:

function functionA() {
function functionB() {
throw new Error("Error!")
}

functionB()
}

functionA()

// Error: Error!
// at functionB (/index.js:3:11)
// at functionA (/index.js:6:3)
// Здесь видно, какие функции вызывали какие,
// и что привело к ошибке, включая номер строки и символа.

Если же наши функции были анонимными, то стек вызовов будет не так полезен:

;(function () {
;(function () {
throw new Error("Error again!")
})()
})()

// Error: Error again!
// at /index.js:13:11
// at /index.js:14:5
// at /index.js:15:3
// Да, тут есть номера строк, но это не так удобно,
// как название функции, по которому можно искать.

Особенно это может быть важно в колбэках — когда мы вызываем какую-то функцию в ответ на событие:

// Анонимная функция будет менее полезна при отладке:
someElement.addEventListener("click", function () {
throw new Error("Error when clicked!")
})

// ...В отличие от именованной:
someElement.addEventListener("click", function someElementClickHandler() {
throw new Error("Error when clicked!")
})

💡 Обычные и стрелочные функции 🔗

Отличие стрелочных функций от обычных в том, что у них нет this. Также стрелочные функции в силу своего синтаксиса анонимны, если не присвоить их переменной.

const arrowFunction = () => {}
// Стрелочная функция записывается сильно короче, чем обычная.
// Ключевое слово function не требуется, так как сама нотация
// "() =>" подразумевает функцию.

// Если функция ничему не присвоена, то она анонимна:
;(() => {
console.log("Hello world")
})()

Так как у них нет this, то внутри нельзя получить доступ в arguments:

const arrow = () => {
console.log(arguments)
}

arrow()
// ReferenceError: arguments is not defined.

Также из-за отсутствия this их нельзя использовать с new.

Стрелочные функции не могут быть функциями-конструкторами.

const Factory = () => {
return {
name: "Arthur",
}
}

const person = new Factory()
// TypeError: Factory is not a constructor.

// С обычной функцией — порядок.
function Factory() {
return {
name: "Arthur",
}
}

const person = new Factory()

В работе 🔗

Миша 👨‍🔧

🛠 При написании функции указываются параметры — те переменные, с которыми работает функция. Но возможны случаи, когда не все параметры заданы. Это может быть и специально, например для использования варианта по умолчанию, так и случайно — ошибка при использовании или неожиданные входные данные.

See the Pen function в работе by vindi-r (@vindi-r) on CodePen.

🛠 Давайте функциям имена, чтобы отладку было проводить проще.

// Анонимную функцию будет сложнее отлаживать,
// потому что в стеке вызовов не будет её имени.
someElement.addEventListener("click", function () {
throw new Error("Error when clicked!")
})

// ...В отличие от именованной.
someElement.addEventListener("click", function someElementClickHandler() {
throw new Error("Error when clicked!")
})

🛠 У стрелочных функций можно использовать быстрый (implicit) return.

const arrowFunc1 = () => {
return 42
}

const arrowFunc2 = () => 42

arrowFunc1() === arrowFunc2()
// Обе функции возвращают 42.

// Также можно возвращать любые структуры и типы даных:
const arrowFunc3 = () => "string"
const arrowFunc4 = () => ["array", "of", "strings"]

// Чтобы вернуть объект, его необходимо обернуть в скобки.
// Только так JS поймёт, что мы не открываем тело функции,
// а возвращаем результат.
const arrowFunc5 = () => ({ some: "object" })

Автор: Миша 👨‍🔧