Tryb ścisły JavaScript („use strict”), wprowadzony w ECMAScript 5, to sposób na pisanie bardziej niezawodnego i bezpiecznego kodu poprzez włączenie bardziej restrykcyjnej odmiany języka, która eliminuje ciche błędy, zapobiega niebezpiecznym operacjom i umożliwia silnikom JavaScript lepsze optymalizacje.
Dyrektywa w formie zwykłego łańcucha znaków „use strict” zmienia sposób parsowania i wykonywania kodu, zamieniając wcześniej akceptowane, problematyczne zachowania na wyraźne błędy — dzięki czemu nie da się np. przypadkowo tworzyć zmiennych globalnych. W nowoczesnym JavaScript tryb ścisły działa automatycznie w modułach ES6 i wewnątrz klas, co sprawia, że jest de facto domyślnym zachowaniem współczesnego kodu, przy jednoczesnym zachowaniu wstecznej kompatybilności ze starszymi skryptami.
Najważniejsze korzyści z włączenia trybu ścisłego to:
- eliminacja cichych błędów – typowe wpadki stają się natychmiastowymi wyjątkami, co przyspiesza debugowanie;
- wymuszenie dobrych praktyk – konieczność jawnych deklaracji i zakaz niebezpiecznych operacji ograniczają zanieczyszczanie zakresu globalnego;
- przewidywalne this – wywołania funkcji bez kontekstu nie „wpadają” w obiekt globalny, co zmniejsza ryzyko błędów;
- bezpieczniejszy eval i arguments – izolacja zakresu i brak powiązań z parametrami upraszczają model wykonania;
- lepsze optymalizacje – kompilatory mogą wykonywać agresywniejsze usprawnienia dzięki jaśniejszym gwarancjom semantycznym.
Historyczne tło i przyczyny wprowadzenia trybu ścisłego
Ewolucja JavaScript i problemy projektu języka
JavaScript powstał w pośpiechu w 1995 roku i zawiera decyzje projektowe, które z dzisiejszej perspektywy okazały się problematyczne dla bezpieczeństwa i wydajności. Wraz z popularyzacją języka w aplikacjach webowych stało się jasne, że niektóre cechy, takie jak niejawne tworzenie zmiennych globalnych czy niejednoznaczne zachowanie this, mogą prowadzić do trudnych do zdiagnozowania błędów w środowisku produkcyjnym. Jednocześnie usunięcie tych funkcji wprost z języka złamałoby działanie milionów istniejących stron.
Zespół standaryzujący ECMAScript 5 stanął przed dylematem: jak ulepszyć język bez łamania istniejącego kodu. Rozwiązaniem było wprowadzenie opcjonalnego trybu ścisłego, który umożliwia nowemu lub migrowanemu kodowi korzystanie z bardziej rygorystycznych reguł bez wpływu na skrypty działające w trybie nieścisłym („sloppy mode”). Zachowano pełną wsteczną kompatybilność, ponieważ starsze silniki, które nie rozumiały dyrektywy "use strict", ignorowały ją jak bezużyteczne wyrażenie i kontynuowały wykonanie w standardowym trybie.
Uwzględnienie przyszłych wersji języka
Oprócz naprawy istniejących problemów tryb ścisły zaprojektowano z myślą o przyszłym rozwoju JavaScript. Rezerwując dodatkowe słowa kluczowe i zakazując pewnych konstrukcji składniowych, umożliwiono ewolucję języka bez ryzyka kolizji nowych funkcji z istniejącym kodem. Miało to szczególne znaczenie w ES6, gdzie wprowadzono m.in. let, const i yield, które mogłyby powodować konflikty w kodzie działającym bez trybu ścisłego.
Zrozumienie dyrektywy trybu ścisłego
Czym dokładnie jest „use strict”
„Use strict” to dyrektywa, a nie instrukcja w tradycyjnym sensie. Jest to dosłowny łańcuch znaków "use strict";, który informuje silnik JavaScript, że kod poniżej (lub wewnątrz funkcji) powinien być wykonywany zgodnie z bardziej rygorystycznymi regułami. Zastosowanie zwykłego łańcucha było celowe, ponieważ starsze silniki traktowały go jako bezużyteczne wyrażenie bez skutków ubocznych. Pozwoliło to stopniowo przechodzić do trybu ścisłego bez łamania wstecznej kompatybilności.
Dyrektywa „use strict” musi znaleźć się na samym początku pliku lub ciała funkcji, w przeciwnym razie zostanie zignorowana i kod będzie działał w trybie nieścisłym. Wymóg ten zapobiega niejednoznaczności i zapewnia przewidywalność.
Zakres zastosowania
Tryb ścisły można włączyć na poziomie globalnym oraz funkcji. Gdy „use strict” pojawia się na początku pliku, cały kod w tym pliku działa w trybie ścisłym. Alternatywnie, umieszczenie „use strict” na początku ciała funkcji włącza go tylko dla tej funkcji; kod poza nią (o ile nie jest w innej funkcji z trybem ścisłym) pozostaje w trybie nieścisłym. To ułatwia stopniową migrację starszych baz kodu.
Jak włączyć tryb ścisły
Zastosowanie globalne
Włączenie trybu ścisłego globalnie wymaga umieszczenia dyrektywy na samym początku pliku JavaScript, przed jakąkolwiek inną instrukcją:
"use strict";
function myFunction() {
x = 10; // Spowoduje błąd ReferenceError
}
myFunction();
W tym przykładzie cały plik działa w trybie ścisłym, a dowolna próba przypisania do niezadeklarowanej zmiennej x spowoduje ReferenceError, co zapobiega niezamierzonemu tworzeniu zmiennych globalnych.
Zastosowanie na poziomie funkcji
Włączenie trybu ścisłego tylko dla konkretnej funkcji daje elastyczność, szczególnie w projektach łączących starszy i nowy kod:
function strictFunction() {
"use strict";
y = 20; // ReferenceError wewnątrz tej funkcji
}
function nonStrictFunction() {
z = 30; // Tworzy zmienną globalną – bez błędu
}
Podejście funkcja po funkcji pozwala wprowadzać tryb ścisły stopniowo i bezpiecznie.
Automatyczne włączenie w nowoczesnym JavaScript
Moduły ES6 ładowane za pomocą import/export oraz wszystko wewnątrz definicji klas działa automatycznie w trybie ścisłym, bez konieczności dodawania dyrektywy.
// Moduł – zawsze w trybie ścisłym
export function myFunction() {
undeclaredVariable = 10; // ReferenceError – nawet bez "use strict"
}
class MyClass {
// Wszystko tutaj jest automatycznie w trybie ścisłym
method() {
this.someValue = 5; // OK – tworzy/ustawia właściwość instancji
}
}
W większości współczesnych aplikacji (moduły ES6, klasy) tryb ścisły jest domyślny, więc dyrektywę warto dodawać głównie w starszych skryptach ładowanych przez <script> bez type="module".
Główne zmiany behawioralne w trybie ścisłym
Zmienne i deklaracje
Jedną z fundamentalnych zmian jest wymóg deklarowania wszystkich zmiennych przed użyciem. W trybie nieścisłym przypisanie do niezadeklarowanej zmiennej tworzy zmienną globalną, co zaśmieca przestrzeń nazw i utrudnia debugowanie.
// Sloppy mode
function createVariable() {
myGlobal = 42;
}
createVariable();
console.log(myGlobal); // 42 – zmienna globalna została utworzona!
// Strict mode
"use strict";
function createVariable() {
myGlobal = 42; // ReferenceError: myGlobal is not defined
}
createVariable();
W trybie ścisłym przypisanie do zmiennej niezadeklarowanej przez var, let lub const powoduje natychmiastowy ReferenceError. Wymusza to świadome deklarowanie i ogranicza zanieczyszczanie zakresu globalnego.
Ponadto tryb ścisły zakazuje duplikowania nazw parametrów funkcji. W takiej sytuacji już podczas parsowania powstanie SyntaxError, zanim kod zostanie uruchomiony.
Właściwości obiektu i operacje zapisu
Tryb ścisły wzmacnia ochronę właściwości obiektów, zabraniając zapisu do właściwości tylko do odczytu i zgłaszając błędy tam, gdzie tryb nieścisły milczy. To ważne dla integralności danych.
"use strict";
const obj = {};
Object.defineProperty(obj, "readOnlyProperty", {
value: 42,
writable: false
});
obj.readOnlyProperty = 100; // TypeError
Tryb ścisły zabrania też usuwania zmiennych, funkcji i argumentów funkcji przez delete. W trybie nieścisłym operacje te kończą się po cichu niepowodzeniem, natomiast w trybie ścisłym generują SyntaxError lub TypeError w zależności od kontekstu.
Zachowanie słowa kluczowego this
W trybie nieścisłym wywołanie funkcji bez jawnego kontekstu powoduje, że this odnosi się do obiektu globalnego (window w przeglądarce, global w Node.js). W trybie ścisłym this pozostaje undefined, o ile nie zostanie jawnie określone.
// Sloppy mode
function showThis() {
console.log(this);
}
showThis(); // window (w przeglądarce)
// Strict mode
"use strict";
function showThis() {
console.log(this);
}
showThis(); // undefined
Dodatkowo, gdy funkcja w trybie ścisłym jest wywoływana przez call lub apply z wartością prymitywną (np. liczba, łańcuch), wartość ta nie jest opakowywana w obiekt „wrappera”, co zmniejsza narzut i czyni zachowanie bardziej przewidywalnym.
Funkcja eval() i obiekt arguments
Tryb ścisły znacząco zmienia zachowanie eval(). W trybie nieścisłym kod wewnątrz eval() może modyfikować zmienne w otaczającym zakresie, utrudniając kompilatorowi optymalizację.
// Sloppy mode
var x = 1;
eval("var x = 2; console.log(x);"); // 2
console.log(x); // 2 – zmienna x została zmieniona przez eval()
// Strict mode
"use strict";
var y = 1;
eval("var y = 2; console.log(y);"); // 2
console.log(y); // 1 – eval() nie zmienił zmiennej y
W trybie ścisłym eval() działa w swoim własnym zakresie i nie „wycieka” do zakresu zewnętrznego, co znacząco ułatwia optymalizację.
Podobnie zmienia się obiekt arguments: w trybie nieścisłym zmiana wartości parametru i odpowiedniej właściwości arguments wpływa na siebie nawzajem; w trybie ścisłym są one niezależne, co upraszcza model wykonania.
Słowa kluczowe zastrzeżone na przyszłość
Tryb ścisły rezerwuje dodatkowe słowa kluczowe, m.in. implements, interface, let, package, private, protected, public, static, yield. Użycie ich jako identyfikatorów skutkuje SyntaxError.
"use strict";
let public = 10; // SyntaxError – 'public' jest zarezerwowane
const interface = {}; // SyntaxError – 'interface' jest zarezerwowane
Dzięki temu kod pisany w trybie ścisłym pozostaje zgodny z przyszłymi wersjami języka.
Liczby ósemkowe i inne konstrukty składni
Tryb ścisły zabrania starej składni liczb ósemkowych z wiodącym zerem (np. 0644). Zamiast tego należy używać nowoczesnego zapisu 0o644.
"use strict";
const octalNumber = 0755; // SyntaxError – liczby ósemkowe niedozwolone
const modernOctal = 0o755; // OK – nowoczesny zapis
Ta zmiana może wydawać się drobna, ale zapobiega subtelnym błędom, gdy ktoś nieświadomie poprzedzi liczbę zerem.
Korzyści bezpieczeństwa i wydajności
Eliminacja cichych błędów
Najbardziej widoczną korzyścią trybu ścisłego jest przechwytywanie typowych błędów programistycznych, które w trybie nieścisłym przechodziłyby bez komunikatu. Niezadeklarowane zmienne, próby zapisu do właściwości tylko do odczytu czy usuwanie nieusuwalnych właściwości są natychmiast sygnalizowane.
W trybie nieścisłym błąd taki jak przypisanie do nieistniejącej zmiennej może ujawniać się dopiero po dłuższym czasie, ponieważ powstaje zmienna globalna. W trybie ścisłym programista dostaje natychmiastową informację zwrotną i może szybko naprawić problem.
Optymalizacje wydajności
Choć może to brzmieć paradoksalnie, tryb ścisły ułatwia silnikom JavaScript agresywniejsze optymalizacje. Gdy wiadomo, że nie wystąpią pewne konstrukcje (np. dynamiczne tworzenie globali, wpływ eval() na zewnętrzny zakres czy niejednoznaczności z with), kompilator może lepiej inline’ować funkcje, optymalizować dostęp do zmiennych i wykonywać więcej transformacji kodu. W wielu przypadkach kod w trybie ścisłym działa szybciej, zwłaszcza w krytycznych fragmentach.
Bezpieczeństwo w kontekście przeglądarki i serwera
Tryb ścisły ogranicza przypadkową modyfikację obiektu globalnego i wbudowanych wartości jak NaN, Infinity czy undefined.
// Sloppy mode – bardzo złe!
NaN = "something"; // Cicho się nie powiedzie, ale może powodować problemy
Infinity = 5; // Cicho się nie powiedzie
// Strict mode
"use strict";
NaN = "something"; // TypeError
Infinity = 5; // TypeError
Tryb ścisły jest także istotny, gdy JavaScript wykonuje się w imieniu użytkowników (np. wtyczki, kod wprowadzany w formularzach), ponieważ zmniejsza ryzyko niezamierzonych lub złośliwych modyfikacji.
Praktyczne przykłady i przypadki użycia
Przypadek 1 – zapobieganie niezamierzonym zmiennym globalnym
Jednym z najczęstszych błędów jest brak deklaracji zmiennej, przez co staje się ona globalna i powoduje kolizje nazw.
// Bez strict mode
function processData(data) {
result = data.map(item => item * 2); // Oops! 'result' jest teraz globalne
return result;
}
// Gdzie indziej w kodzie
let result = "original"; // Inna zmienna 'result'
processData([1, 2, 3]);
console.log(result); // Nieoczekiwanie: [2, 4, 6], a nie "original"!
// Ze strict mode
"use strict";
function processData(data) {
result = data.map(item => item * 2); // ReferenceError! Błąd natychmiast
}
W trybie ścisłym problem wychwytywany jest od razu, co oszczędza długiego debugowania.
Przypadek 2 – ochrona właściwości obiektu
Tryb ścisły pomaga chronić integralność danych, gdy obiekt powinien być niezmienny.
"use strict";
const user = { name: "John", age: 30 };
// Zamroź obiekt, aby żaden kod nie mógł go zmodyfikować
Object.freeze(user);
user.name = "Jane"; // TypeError w trybie ścisłym
user.age = 31; // TypeError w trybie ścisłym
W trybie nieścisłym powyższe przypisania cicho by się nie powiodły; w trybie ścisłym błąd jest wyraźny.
Przypadek 3 – funkcje i zakresy parametrów
Tryb ścisły zapobiega użyciu zduplikowanych nazw parametrów, które prowadzą do niejednoznaczności.
// Sloppy mode – niejasne zachowanie
function sum(a, a, c) {
return a + a + c; // Który 'a'? Drugi (ostatni)!
}
console.log(sum(1, 2, 3)); // 7, a nie 6!
// Strict mode
"use strict";
function sum(a, a, c) { // SyntaxError!
return a + a + c;
}
Wymuszenie unikalnych nazw parametrów skutkuje czytelniejszym i bezpieczniejszym kodem.
Nowoczesny JavaScript i tryb ścisły
Automatyczne włączenie w modułach ES6
Wszystkie moduły ES6 automatycznie działają w trybie ścisłym, bez potrzeby ręcznego dodawania dyrektywy.
// modern-app.js – to automatycznie jest w trybie ścisłym
import { UserService } from './services/user-service.js';
export async function fetchUser(id) {
const service = new UserService();
undeclaredVariable = 10; // ReferenceError – nawet bez "use strict"!
return await service.getUser(id);
}
Nowy kod jest domyślnie bezpieczniejszy i łatwiejszy do optymalizacji, bez dodatkowych zmian w składni.
Klasy i ich ścisły tryb działania
Definicje klas w JavaScript zawsze działają w trybie ścisłym — niezależnie od otaczającego kodu. Wszystkie metody, konstruktory i inicjalizatory właściwości wewnątrz klasy korzystają z rygorystycznych reguł.
class UserRepository {
constructor() {
// Wszystko tutaj jest automatycznie w strict mode
this.users = []; // OK – inicjalizacja właściwości
}
addUser(user) {
// Metoda jest w strict mode
this.users.push(user); // OK
undeclaredVar = user; // ReferenceError – nawet bez explicit "use strict"
}
}
// Kod poza klasą może być w sloppy mode
globalVar = "something"; // OK w sloppy mode
Korzystając z nowoczesnych funkcji (moduły, klasy), automatycznie zyskujesz ochronę trybu ścisłego.
Transpilery i bundlery
Nowoczesne narzędzia, takie jak Webpack, Babel i Rollup, często automatycznie dodają "use strict" do transpilowanego lub zbundlowanego kodu. Dzięki temu nawet starszy kod może korzystać z zalet trybu ścisłego po przetworzeniu w łańcuchu build.
Migracja i najlepsze praktyki
Stopniowa migracja starszych baz kodu
Przy migracji starszego kodu do trybu ścisłego najlepsze jest podejście stopniowe — funkcja po funkcji lub plik po pliku. Pozwala to identyfikować i naprawiać problemy w małych, zarządzalnych krokach, bez przytłaczającej liczby błędów na raz.
// Starszy plik – najpierw bez strict mode
function oldFunction() {
// Stary kod bez strict mode
result = 42; // Globalna zmienna
}
function functionToUpgrade() {
"use strict"; // Nowy kod w strict mode
let result = 42; // Lokalna zmienna
}
function anotherFunction() {
// Jeszcze nie zmigrowana
anotherResult = 43; // Globalna zmienna
}
Takie podejście ogranicza ryzyko niezamierzonych konsekwencji i ułatwia testowanie każdego segmentu niezależnie.
Najlepsze praktyki dla nowych projektów
Dla nowych projektów warto:
- natychmiast włączyć strict mode – albo poprzez umieszczenie dyrektywy na początku każdego pliku skryptu, albo poleganie na automatycznym włączeniu przez moduły ES6;
- używać modułów ES6 – struktura modułów zapewnia automatyczny tryb ścisły oraz lepszą organizację i kontrolę zależności;
- konfigurować lintera – użyj narzędzia takiego jak ESLint, aby egzekwować reguły i ostrzegać o kodzie niezgodnym z trybem ścisłym;
- typować przy użyciu TypeScript – TypeScript dodaje walidacje podobne do trybu ścisłego, a do tego zapewnia pełną kontrolę typów.
Dzięki tym praktykom kod od początku jest bezpieczniejszy i lepiej optymalizowalny.