Use strict

JavaScript jest językiem, który musi zachowywać kompatybilność z kolejnymi wersjami. Oznacza to, że w JavaScript nie można wycofać zmian, które okazały się błędem. Głównym założeniem jest przede wszystkim zapewnienie działania tych skryptów, które zostały już napisane.

W wersji ES5 pojawiły się jednak pewne zmiany, których zadaniem było ulepszenie JavaScript. Zmiany te były standardowo wyłączone, więc nadal język zachowywał kompatybilność z innymi wersjami. Dano nam jednak możliwość decydowania o tym czy nasza aplikacja będzie używała wprowadzonych zmian i do tego powstała dyrektywa 'use strict'. Można powiedzieć, że w ten sposób włączamy bezpieczniejszą i lepszą wersję JavaScript.

Włączenie tak po prostu trybu strict w starszym kodzie mógłby doprowadzić do zgłoszenia wielu błędów przez JavaScript i aplikacja nie działałaby dopóki nie wnieślibyśmy do niej kilku poprawek, które teraz sobie omówimy.

Dyrektywa use strict

Dyrektywę use strict umieszczamy na początku pliku. Wszystko co znajduje się pod dyrektywą działa w trybie ścisłym:

'use strict';

/// some code
1
2
3

Co ciekawe, możemy także użyć trybu ścisłego wewnątrz funkcji.

(function() {
  'use strict';

  // ...your code here...
})();
1
2
3
4
5

W tym przypadku tryb strict włączony został tylko dla tej funkcji. Kod poniżej tej dyrektywy będzie musiał przestrzegać trybu ścisłego.

Należy pamiętać, że dyrektywa use strict musi być umieszczona na początku pliku lub funkcji:

a = 'foo';
// no strict mode

"use strict";
b = 'bar';
// no strict mode

console.log(a); // foo
console.log(b); // bar
1
2
3
4
5
6
7
8
9

Ten kod w ogóle nie działa w trybie ścisłym. Przed use strict nie możemy umieszczać żadnego kodu. Możemy umieszczać tylko komentarze.

Powyższy kod nie jest prawidłowy w trybie ścisłym, a jednak wykonuje się pomimo umieszczonej dyrektywy. Dzieje się tak, bo tryb ścisły nie został włączony. Deklaracja dla trybu ścisłego musi znaleźć się przed pierwszą linią kodu w skrypcie lub funkcji inaczej jest ignorowana.

Zmienne muszą być zadeklarowane:

name = 'foo';
x = { p1: 10, p2: 20 };  
1
2

Nie można używać zmiennych bez deklaracji var, let lub const. Zmienna deklarowana w taki sposób w jakimkolwiek miejscu w kodzie, od razu stawała się globalna. Na szczęście tryb ścisły ogranicza tą możliwość.

Nie można używać delete do usuwania zmiennych, funkcji ani obiektów:

a = 3.14;
a1 = function() {
}
a3 = { o: 42 };
delete a;
delete a1;
delete a3;
1
2
3
4
5
6
7

W trybie ścisłym nie ma możliwości usuwania niezadeklarowanych zmiennych. Jeżeli istnieją z jakiegoś powodu, to nie można ich usuwać.

Nadal jednak można używać delete do usuwania wewnętrznych właściwości obiektu.

Zabronione jest duplikowanie parametrów funkcji i pól obiektów:

function x(p1, p1) {
};
const o = { p: 1, p: 2 }; 
1
2
3

Niestety taki kod gdzie mamy zduplikowane nazwy parametrów i pola obiektu jest poprawny w JavaScript. Dopiero tryb strict wyklucza możliwość stworzenia funkcji o dwóch takich samych parametrach.

Dawniej liczby ósemkowe można było deklarować za pomocą zera z przodu liczby:

const octal = 010;
1

Taka deklaracja jest niepoprawna, jak wiemy liczby ósemkowe zapisujemy z notacją zero i literką o:

const octal2 = 0o10;
1

Dopiero taka deklaracja jest poprawna. Jednak przed trybem ścisłym można było zapisać deklarację tylko z zerem na początku.

Nie można przypisywać wartości do natywnych właściwości języka:

var undefined = 5;
var Infinity = 5;
1
2

JavaScript jest tak elastycznym językiem, że można zrobić nawet takie cuda i nadpisać natywną wartość undefined czy Infinity i wiele innych. Teraz jest to zabronione i zgłaszany jest błąd.

Przy pracy z obiektami, w trybie strict JavaScript zgłasza błędy, gdy próbujemy wykonać niedozwoloną operację:

Zdefiniuję teraz pusty obiekt:

const obj1 = {};
Object.defineProperty(obj1, 'x', { value: 0, writable: false });
1
2

Za pomocą metody defineProperty deklaruję w obiekcie pole x oraz przypisuję mu wartość 0, a także oznaczam, że pole to jest tylko do odczytu:

obj1.x = 42;
1

Próbuję nadpisać wartość x. Pomimo tego, że wartość pola x nie zostanie nadpisana, to i tak JavaScript nie zgłaszał błędu. Teraz w trybie ścisłym jest zgłaszany błąd.

Zabronione jest przypisywanie wartości dla getterów:

Stworzymy sobie kolejny obiekt, z getterem:

const obj2 = {
  get x() {
    return 17;
  }
}; 
1
2
3
4
5

Getter powinien tylko zwracać wartość:

obj2.x = 5;
1

JavaScript traktuje metodę obiektu obj2 jako pole tego obiektu i pozwala na zapis przypisania wartości. W trybie ścisłym taki zapis powoduje wyrzucenie błędu.

Stworzenie nowego pola w obiekcie pomimo zakazu jego rozszerzania:

Tworzę obiekt, i za pomocą metody preventExtensions, oznaczam, że obiekt nie może być rozszerzony o nowe pola:

var obj3 = {};
Object.preventExtensions(obj3);

1
2
3

Mogę jednak spróbować dodać nowe pole.

obj3.newProp = 'foo'; 
1

Do tej pory wykonanie takich operacji i tak nie miało żadnego skutku, ale też nie zgłaszało błędu. Wydawało się więc, że kod jest poprawny. Teraz JavaScript zgłasza wyjątki w trybie strict.

Próba usuwania wartości nieusuwalnych również zgłasza błąd:

Można było użyć delete do usuwania predefiniowanych właściwości języka:

delete Object.prototype;
1

Mogliśmy stworzyć zapis, który usuwa prototype z globalnego obiektu Object. Chociaż kod te nie miał efektu, to nie zgłaszał też żadnego błędu. Teraz otrzymujemy wyjątek.

Nie można dodawać pól do wartości prymitywnych:

Do obiektu false dodaje pole true, lub do literału 41 dodaje pole name:

false.true = 'foo';
(41).name = 'boo';
1
2

Przy takich dziwnych zapisach przed trybem strict JavaScript nie zgłaszał żadnych błędów. Dla trybu strict takie zapisy nie są już możliwe.

Zakaz używania słów kluczowych dla języka jako nazwy zmiennych:

Kolejną dziwną sytuacją bez trybu ścisłego jest możliwość zadeklarowania zmiennej o nazwie let albo public:

var let = 42;
var public = 42;
1
2

Poza trybem strict można było używać niektórych słów kluczowych do deklaracji zmiennych. W specyfikacji istnieje teraz lista słów, które są ważne dla języka i będą istotne w przyszłości, są teraz zabronione.

W funkcjach zadeklarowanych globalnie this jest teraz undefined:

Gdy tworzyliśmy funkcję, mogliśmy się odwołać w niej do globalnego obiektu:

function test() {
  console.log(this) // undefined in strict mode
}
1
2
3

Poza trybem strict w funkcji przez this mieliśmy dostęp do globalnego obiektu. W przeglądarce był to obiekt window . Teraz this w funkcji zwraca undefined.

Eval oraz with

Dodatkowo tryb strict reguluje funkcję eval oraz instrukcję with. Jednak tego tematu już nie będę omawiał. Raczej nie powinniśmy używać tych właściwości języka JavaScript w naszym kodzie.

Poznaliśmy kilka przypadków, w których JavaScript działał naprawdę dziwnie i poza wszelką intuicją. Tryb strict wymusza poprawne działanie i zgłaszanie wyjątków w sytuacjach, kiedy powinny być zgłoszone. Język w tym trybie jest czytelniejszy i bezpieczniejszy.

Czy i jak używać use strict

Czy zawsze powinniśmy używać trybu strict. Krótka odpowiedź brzmi jak najbardziej. Chroni to nas przed popełnieniem błędu i sam kod jest o wiele bardziej bezpieczny. Są jednak wyjątki, w których nie musimy używać tej dyrektywy.

  • klasy JavaScript domyślnie pracują w trybie strict
  • moduły ES6 w JavaScript także domyślnie pracują w trybie strict
  • w żadnym nowoczesnym frameworku nie potrzebujesz włączać use strict

Pisząc dzisiaj nowoczesny kod w JavaScript, gdzie używa się frameworków, modułów ES6 i klas nie musimy dopisywać na początku naszego pliku use strict.

Jeśli jednak pracujesz z bardzo starym kodem lub po prostu używasz Vanilla JS i nie używasz modułów, klas to włączenie tego trybu pomoże Ci pisać lepszy kod.

Ja tworząc przykłady dla tego kursu, nie używam trybu strict, głównie po to, aby Wam pokazać pełne działanie JavaScript.

Co warto zapamiętać

  • tryb ścisły reguluje i poprawia błędy JavaScript

  • aby zachować kompatybilność języka ze starymi aplikacjami, tryb ten nie jest domyślny

  • klasy JavaScript domyślnie pracują w trybie strict

  • moduły ES6 w JavaScript także domyślnie pracują w trybie strict

  • w żadnym nowoczesnym frameworku nie potrzebujesz włączać use strict