Какие знаешь способы объявления функции и в чем их различия?

👨‍💻 Frontend Developer 🟡 Часто попадается 🎚️ Средний
#JavaScript #функции #База JS

Краткий ответ

В JavaScript существует 5 основных способов объявления функций:

  1. Function Declaration — объявление функции
  2. Function Expression — функциональное выражение
  3. Arrow Function — стрелочная функция (ES6)
  4. Method Definition — метод объекта
  5. Constructor Function — функция-конструктор

Ключевые различия:

  • Hoisting (поднятие)
  • Контекст this
  • Синтаксис и читаемость
  • Возможность использования как конструктор

Способы объявления функций

1. Function Declaration (Объявление функции)

Классический способ объявления функции.

function greet(name) {
  return `Привет, ${name}!`;
}
 
console.log(greet("Александр")); // "Привет, Александр!"

Особенности:

  • Hoisting — функция доступна до объявления
  • Создает именованную функцию
  • Может использоваться как конструктор
  • Имеет собственный контекст this
// Работает благодаря hoisting!
console.log(sayHello("Мир")); // "Привет, Мир!"
 
function sayHello(name) {
  return `Привет, ${name}!`;
}

2. Function Expression (Функциональное выражение)

Функция как значение переменной.

const greet = function(name) {
  return `Привет, ${name}!`;
};
 
console.log(greet("Александр")); // "Привет, Александр!"

Особенности:

  • Нет hoisting — доступна только после объявления
  • Может быть анонимной или именованной
  • Может использоваться как конструктор
  • Имеет собственный контекст this
// Ошибка! sayHello не определена
// console.log(sayHello("Мир")); // ReferenceError
 
const sayHello = function(name) {
  return `Привет, ${name}!`;
};
 
// Именованное функциональное выражение
const factorial = function fact(n) {
  return n <= 1 ? 1 : n * fact(n - 1);
};

3. Arrow Function (Стрелочная функция)

Краткий синтаксис функций, введенный в ES6.

// Обычная стрелочная функция
const greet = (name) => {
  return `Привет, ${name}!`;
};
 
// Краткая запись (неявный return)
const greetShort = name => `Привет, ${name}!`;
 
// Без параметров
const sayHello = () => "Привет!";
 
// Несколько параметров
const add = (a, b) => a + b;
 
console.log(greet("Александр")); // "Привет, Александр!"
console.log(add(5, 3));         // 8

Особенности:

  • Нет hoisting
  • Лексический this — наследует контекст
  • Нельзя использовать как конструктор
  • Нет собственного arguments
  • Краткий синтаксис
const obj = {
  name: "Александр",
  
  // Обычная функция
  regularMethod: function() {
    console.log(this.name); // "Александр"
    
    const inner = function() {
      console.log(this.name); // undefined (this = window/global)
    };
    inner();
  },
  
  // Стрелочная функция
  arrowMethod: function() {
    console.log(this.name); // "Александр"
    
    const inner = () => {
      console.log(this.name); // "Александр" (наследует this)
    };
    inner();
  }
};

4. Method Definition (Метод объекта)

Краткий синтаксис для методов объектов (ES6).

const person = {
  name: "Александр",
  age: 30,
  
  // Краткий синтаксис метода
  greet() {
    return `Привет, я ${this.name}!`;
  },
  
  // Эквивалентно:
  greetOld: function() {
    return `Привет, я ${this.name}!`;
  }
};
 
console.log(person.greet()); // "Привет, я Александр!"

Особенности:

  • Краткий синтаксис
  • Имеет собственный контекст this
  • Может использоваться как конструктор
  • Поддерживает super в классах

5. Constructor Function (Функция-конструктор)

Функция для создания объектов.

function Person(name, age) {
  this.name = name;
  this.age = age;
  
  this.greet = function() {
    return `Привет, я ${this.name}!`;
  };
}
 
// Создание объекта
const person = new Person("Александр", 30);
console.log(person.greet()); // "Привет, я Александр!"
 
// Проверка типа
console.log(person instanceof Person); // true

Особенности:

  • Вызывается с оператором new
  • this указывает на создаваемый объект
  • Автоматически возвращает созданный объект
  • Устанавливает прототип

Сравнение способов объявления

СпособHoistingthisКонструкторСинтаксис
Function Declaration✅ ДаДинамический✅ Даfunction name() {}
Function Expression❌ НетДинамический✅ Даconst name = function() {}
Arrow Function❌ НетЛексический❌ Нетconst name = () => {}
Method Definition❌ НетДинамический✅ ДаmethodName() {}
Constructor Function✅ ДаНовый объект✅ Даfunction Name() {}

Контекст this в функциях

Обычные функции

function regularFunction() {
  console.log(this); // зависит от способа вызова
}
 
// Глобальный вызов
regularFunction(); // window (браузер) или global (Node.js)
 
// Вызов как метод
const obj = { method: regularFunction };
obj.method(); // obj
 
// Явная привязка
regularFunction.call({ name: "test" }); // { name: "test" }

Стрелочные функции

const arrowFunction = () => {
  console.log(this); // всегда лексический контекст
};
 
const obj = {
  name: "Александр",
  
  regularMethod() {
    console.log(this.name); // "Александр"
    
    const arrow = () => {
      console.log(this.name); // "Александр" (наследует от regularMethod)
    };
    
    arrow();
  }
};

Задачи для практики

Задача 1

console.log(typeof foo);
console.log(typeof bar);
 
function foo() {}
var bar = function() {};
Ответ "function" и "undefined" — function declaration поднимается полностью, а var поднимается только объявление.

Задача 2

const obj = {
  name: "Александр",
  
  regular: function() {
    return this.name;
  },
  
  arrow: () => {
    return this.name;
  }
};
 
console.log(obj.regular());
console.log(obj.arrow());
Ответ "Александр" и undefined — стрелочная функция не имеет собственного this и наследует его из глобального контекста.

Задача 3

function Person(name) {
  this.name = name;
}
 
const person1 = new Person("Александр");
const person2 = Person("Мария");
 
console.log(person1);
console.log(person2);
console.log(window.name); // в браузере
Ответ person1 — объект Person с name: "Александр", person2 — undefined, window.name — "Мария". Без new функция выполняется в глобальном контексте.

Задача 4

const calculator = {
  value: 0,
  
  add: function(num) {
    this.value += num;
    return this;
  },
  
  multiply: (num) => {
    this.value *= num;
    return this;
  }
};
 
calculator.add(5).multiply(2);
console.log(calculator.value);
Ответ Код выполнится частично с ошибкой — метод `add(5)` сработает и увеличит `value` до 5, но при вызове `multiply(2)` произойдет ошибка, так как стрелочная функция `multiply` не имеет доступа к `this` объекта `calculator`. В результате `this.value` будет `undefined`, операция `undefined *= 2` даст `NaN`, и цепочка методов прервется.

Задача 5

function createCounter() {
  let count = 0;
  
  return {
    increment: function() {
      count++;
      return count;
    },
    
    decrement: () => {
      count--;
      return count;
    }
  };
}
 
const counter = createCounter();
console.log(counter.increment()); // ?
console.log(counter.decrement()); // ?
Ответ 1 и 0 — обе функции имеют доступ к переменной count через замыкание, тип функции не влияет на доступ к внешним переменным.

Практические советы

1. Выбор способа объявления

// Для обычных функций
function calculateSum(a, b) {
  return a + b;
}
 
// Для коротких функций и колбэков
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(n => n * 2);
 
// Для методов объектов
const user = {
  name: "Александр",
  
  greet() {
    return `Привет, ${this.name}!`;
  }
};
 
// Для конструкторов (лучше использовать классы)
class Person {
  constructor(name) {
    this.name = name;
  }
  
  greet() {
    return `Привет, я ${this.name}!`;
  }
}

2. Когда использовать стрелочные функции

// ✅ Хорошо: колбэки и короткие функции
const users = ['Александр', 'Мария', 'Иван'];
const greetings = users.map(name => `Привет, ${name}!`);
 
// ✅ Хорошо: сохранение контекста this
class Timer {
  constructor() {
    this.seconds = 0;
  }
  
  start() {
    setInterval(() => {
      this.seconds++; // this указывает на экземпляр Timer
    }, 1000);
  }
}
 
// ❌ Плохо: методы объектов
const obj = {
  name: "Александр",
  greet: () => {
    return `Привет, ${this.name}!`; // this не указывает на obj
  }
};

3. Избегайте частых ошибок

// ❌ Плохо: потеря контекста
const person = {
  name: "Александр",
  greet() {
    return `Привет, ${this.name}!`;
  }
};
 
const greetFunc = person.greet;
console.log(greetFunc()); // "Привет, undefined!"
 
// ✅ Хорошо: привязка контекста
const boundGreet = person.greet.bind(person);
console.log(boundGreet()); // "Привет, Александр!"
 
// ✅ Или использование стрелочной функции
const arrowGreet = () => person.greet();
console.log(arrowGreet()); // "Привет, Александр!"

Резюме

  • Function Declaration — классический способ, поднимается, имеет динамический this
  • Function Expression — функция как значение, не поднимается
  • Arrow Function — краткий синтаксис, лексический this, нельзя использовать как конструктор
  • Method Definition — краткий синтаксис для методов объектов
  • Constructor Function — для создания объектов (лучше использовать классы)

Ключевые различия:

  • Hoisting влияет на доступность функции
  • Контекст this определяет поведение функции
  • Синтаксис влияет на читаемость кода

Понимание различий между способами объявления функций — это основа для написания качественного JavaScript кода!


Хочешь больше статей по подготовке к собеседованию? Подпишись на EasyAdvice(@AleksandrEmolov_EasyAdvice), добавляй сайт в избранное и прокачивай себя каждый день 💪