Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Статичні методи в JavaScript». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Статичні методи в JavaScript** - це функції, прикріплені до конструктора класу, а не до екземплярів чи прототипу. ```javascript class MathUtils { static sum(a, b) { return a + b; } } MathUtils.sum(3, 4); // 7 new MathUtils().sum(); // TypeError ``` **Головне:** використовуй static, коли логіка не потребує стану екземпляра.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Статичні методи в JavaScript** - це функції, прикріплені безпосередньо до конструктора класу, а не до екземплярів чи прототипу. Їх викликають через ім'я класу, без `new`. ## Теорія ### TL;DR - Аналогія: як `Math.max()` - викликаєш `Math`, а не екземпляр Math - `НазваКласу.метод()` працює; `екземпляр.метод()` кидає TypeError - Статичні методи зберігаються на конструкторі класу, минаючи ланцюжок прототипів - Використовуй, коли логіці не потрібен `this` екземпляра - Правило вибору: немає стану екземпляра - роби статичним ### Швидкий приклад ```javascript class Calculator { static add(a, b) { // Прикріплено до Calculator, не до прототипу return a + b; } } console.log(Calculator.add(3, 4)); // 7 const calc = new Calculator(); calc.add(3, 4); // TypeError: calc.add is not a function ``` Метод `add` не торкається даних екземпляра, тому належить класу. Пошук через екземпляр провалюється, бо статичні методи взагалі не потрапляють у прототип. ### Ключова різниця Методи екземплярів зберігаються на `Class.prototype` і доступні кожному об'єкту, створеному через `new`. Статичні методи зберігаються безпосередньо на функції-конструкторі. Тому `calc.__proto__` не має властивості `add`, звідси й TypeError. Це не баг і не дивна поведінка - так і задумано специфікацією. ### Коли використовувати - Математичні або рядкові утиліти без стану: `MathUtils.sum(a, b)`, `StringUtils.slugify(str)` - Фабричні методи (factory methods), що створюють екземпляри з сирих даних: `Person.fromString('Alice, 30')` - Загальні хелпери, які зручно згрупувати під ім'ям класу, а не розкидати як вільні функції - Валідатори, що повторно використовуються в різних маршрутах: `RouteValidator.validateUserId(id)` в Express ### Як це працює всередині V8 зберігає статичні методи як звичайні властивості на функції-конструкторі під час обробки класу, а не на `Class.prototype`. Коли ти викликаєш `MyClass.staticMethod()`, рушій виконує прямий пошук властивості на конструкторі й прив'язує `this` до самого класу. Жодного обходу ланцюжка прототипів, жодного виділення пам'яті під екземпляр. ### Поширені помилки **Виклик статичного методу через екземпляр** ```javascript const utils = new MathUtils(); utils.sum(1, 2); // TypeError: utils.sum is not a function ``` Статичні методи знаходяться на класі, а не на `Class.prototype`. Екземпляр не має до них жодного шляху. Рішення: `MathUtils.sum(1, 2)`. **Очікування, що `this` всередині статика - це екземпляр** ```javascript class Example { static whoami() { console.log(this); } } Example.whoami(); // Виводить клас Example, а не undefined ``` Всередині статичного методу `this` - це конструктор класу, а не екземпляр і не `undefined`. Розробники, що прийшли з Java або Python, часто очікують `null`. Краще взагалі не покладатися на `this` у статичних методах і передавати дані параметрами. **Мутабельний спільний стан через статичні властивості** ```javascript class Counter { static count = 0; static increment() { this.count++; } } Counter.increment(); Counter.increment(); // Counter.count тепер 2, глобально для всіх ``` Цей патерн ламає тести частіше, ніж здається - особливо коли тести запускаються паралельно й використовують один клас. Для ізольованого стану використовуй властивості екземпляра або closure. **Спадкування без `super`** ```javascript class Animal { static getSpecies() { return 'Animal'; } } class Dog extends Animal {} console.log(Dog.getSpecies()); // 'Animal', а не 'Dog' ``` Статичний метод успадкований від `Animal`, тому `Dog` отримує версію батька. Якщо підклас має повертати своє значення, потрібен override або виклик через `super`: ```javascript class Bird extends Animal { static getSpecies() { return super.getSpecies() + ' (Bird)'; } } console.log(Bird.getSpecies()); // 'Animal (Bird)' ``` ### Де зустрічається в реальних проєктах - `Math.max()`, `Math.min()`, `Array.isArray()`, `Promise.all()` - стандартна бібліотека побудована на статиках - `path.join()` у Node.js - чиста утиліта без потреби в екземплярі - `express.json()` - фабрика middleware у стилі статичного методу - `Date.now()` - повертає timestamp без створення об'єкта Date - Утиліти Lodash на кшталт `_.debounce()` слідують тому ж патерну: без стану, без екземпляра ### Питання на співбесіді **Q:** Чим статичні методи відрізняються від методів прототипу на рівні реалізації? **A:** Методи прототипу зберігаються на `Class.prototype` й доступні через ланцюжок прототипів під час виклику `instance.method()`. Статичні методи - це властивості безпосередньо на конструкторі. Для статиків обходу ланцюжка немає. **Q:** Чи може статичний метод отримати доступ до властивостей екземпляра? **A:** Напряму - ні. Всередині статика `this` - це клас, а не екземпляр. Якщо потрібні дані об'єкта, передай його аргументом: `MyClass.process(someInstance)`. **Q:** Що таке `this` всередині статичного методу? **A:** Функція-конструктор класу. Тобто `MyClass.someStatic()` прив'язує `this` до `MyClass`, а не до жодного об'єкта, створеного через `new`. **Q:** Коли краще використати звичайну функцію замість статичного методу? **A:** Коли хелпер не пов'язаний з конкретним класом. Статичний метод виправданий, коли групування під іменем класу додає зрозумілості. Якщо ні - вільна функція простіша. **Q:** (Senior) Поясни спадкування статичних методів і ключове слово `super` у статиках. **A:** Підкласи успадковують статичні методи батька через ланцюжок прототипів на рівні конструкторів: `Dog.__proto__ === Animal`. Тому `Dog.getSpecies()` знаходить `Animal.getSpecies`. В override можна викликати `super.getSpecies()`, щоб явно дістатися батьківського статика. ## Приклади ### Фабричний метод: створення екземпляра з рядка ```javascript class Person { constructor(name, age) { this.name = name; this.age = age; } static fromString(str) { const [name, age] = str.split(', '); return new Person(name, parseInt(age, 10)); } } const alice = Person.fromString('Alice, 30'); console.log(alice.name); // 'Alice' console.log(alice.age); // 30 ``` `fromString` створює `Person`, не розкриваючи сигнатуру конструктора. Логіка створення в одному місці, і пізніше можна додати `Person.fromJSON(json)` чи інші фабрики без зміни точок виклику. ### Валідатор вхідних даних у маршруті Express ```javascript class RouteValidator { static validateUserId(id) { const parsed = Number(id); if (!Number.isInteger(parsed) || parsed <= 0) { throw new Error('Invalid user ID'); } return parsed; } } app.get('/users/:id', (req, res) => { try { const userId = RouteValidator.validateUserId(req.params.id); res.json({ userId }); } catch (err) { res.status(400).json({ error: err.message }); } }); // GET /users/123 -> { userId: 123 } // GET /users/abc -> 400 { error: 'Invalid user ID' } ``` Жодного екземпляра `RouteValidator` не створюється. Логіка валідації згрупована під зрозумілим ім'ям і повторно використовується в усіх маршрутах, де вона потрібна. Це найпоширеніший реальний патерн для статичних методів у Node.js бекендах.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.