A Javascript programozási nyelv

Strict mód

Bevezetés

A strict mód a JavaScript nyelv szigorított változata, amit az ECMAScipt 5 szabványban specifikáltak, és a JavaScript 1.8.5-ös változatában adtak a nyelvhez. Használata opcionális, egy kódban lehetnek strict és nem strict részek is (egy fügvényen belül viszont nem). A strict JavaScript nem egyszerűen részhalmaza a hagyományos JavaScript nyelvnek, hanem egyes nyelvi elemek jelentését is megváltozatja, ezért meglévő kód portolásakor ajánlott a programot újra tesztelni.

Több okból érdemes mégis használni ezt a módot:

Használata

A programozónak lehetősége van egyes függvényeket vagy egész szkripteket strict módúnak deklarálnia. (De pélául az egyes blokkok nem deklarálhatók strictnek.) Mindkét esetben a szkript vagy függvény első utasításának a "use strict"; utasításnak kell lennie. Mivel a JavaScriptben a (string) kifejezések érvényes utasítások, a scrict módot nem ismerő értelmezők ezt a sort kiértékelik, de a program későbbi futására nem lesz hatással.

Szkriptekre

Ha HTML oldalba ágyazunk be JavaScript kódot, a strict mód csak az adott <script> ... </script> tagen belül érvényes.

<script> //az egész szkript strict lesz "use strict"; var hello = "Hello strict mode!"; </script>

A strict mód ilyen használata nem javasolt, mivel strict és normál módú szkriptek összefűzésekor a strict mód az egész összefűzött szkriptre érvényes lesz, ez pedig hibát okozhat a normál módra megírt részben. (A normál módot nem lehet explicit deklarálni.) Helyette érdemes függvényenként megadni a strict módot.

Függvényekre

Függvény szintű strict módot a függvény törzsében, minden más utasítás előtt kell deklarálni. A deklaráció a függvénybe beágyazott más függvényekre is érvényes lesz.

function strict() { // Function-level strict mode syntax 'use strict'; function nested() { return "And so am I!"; } return "Hi! I'm a strict mode function! " + nested(); } function notStrict() { return "I'm not strict."; }

Különbség a normál módhoz képest

Hibák jelzése

A JavaScript nyelvet úgy tervezték, hogy kezdő fejlesztőknek is könnyen használható legyen, sok olyan helyzetben, ahol hibát kellene jeleznie, figyelmen kívül hagyja azt, és folytatja a futást. Ez később gyakran nehezen felderíthető hibákhoz vezet. Strict módban a hibás kódnál kivétel váltódik ki.

Az egyik ilyen eset, amikor a felhasználó olyan változónak próbál értéket adni, amit nem deklarált előzőleg a var kulcsszóval (például elgépelés miatt). Ekkor normál módban létrejön a globális objektumnak egy ilyen nevű attribútuma, és ez veszi fel az értéket. Strict módban ezzel szemben ReferenceError kivétel váltódik ki.

"use strict"; mistypedVaraible = 17; // ReferenceError-t dob

Akkor is kivételt kapunk, ha olyan változót próbálunk felülírni, ami nem írható. Az értékadás normál módban is sikertelen lenne, erről azonban nem kapnánk értesítést.

"use strict"; // Értékadás nem írható attribútumnak var obj1 = {}; Object.defineProperty(obj1, "x", { value: 42, writable: false }); obj1.x = 9; // TypeError-t dob // Értékadás csak getterel elérhető attribútumnak var obj2 = { get x() { return 17; } }; obj2.x = 5; // TypeError-t dob // Értékadás nem kiterjeszthető objektum nem létező attribútumának var fixed = {}; Object.preventExtensions(fixed); fixed.newProp = "ohai"; // TypeError-t dob

Hasonlóan, a nem törölhető attribútumok törlése is hibát eredményez.

"use strict"; delete Object.prototype; // TypeError-t dob

Az objektumok attribútum neveinek egyedinek kell lenniük. Normál módban lehetőség van több azonos nevű attribútumot megadni, ekkor az utolsó értéke számít. Strict módban ez szintaktikai hiba.

"use strict"; var o = { p: 1, p: 2 }; // !!! syntax error

A függvény argumentumok neveinek is egyedinek kell lenniük. Normál módban a paraméterlistában utoljára megadott érték elrejti az előzőeket, de az arguments tömbön keresztül ezek továbbra is elérhetőek. Strict módban ez is szintaktikai hiba.

function sum(a, a, c) // !!! syntax error { "use strict"; return a + b + c; // hibás lenne, ha lefutna }

Strict módban nem használhatunk oktális számokat. (Az ECMAScript szabványban nem is szerepelnek, de a böngészők támogatják.) Normál módban a 0-val kezdődő számokat nyolcas számrendszer beli számként értelmezik, ez könnyen hibához vezethet, ha a programozó nem tud róla. Ezért strict módban nem kezdhetünk számot 0-val.

"use strict"; var sum = 015 + // !!! syntax error 197 + 142;

Változók használatának egyszerűsítése

A strict mód egyszerűsíti a változó nevek definíciókhoz rendelését. Így sokszor ez a hozzárendelés már a futási idő előtt meghatározható lesz, ezzel megkönnyítve az optimalizációt.

Az egyik ilyen egyszerűsítés a with utasítás tiltása. A with-n belüli változónevekről lehetetlen előre eldönteni, hogy a megadott objektum attribútuma, a befoglaló függvény lokális változója vagy globális változó-e.

"use strict"; var x = 17; with (obj) // !!! syntax error { //ez az x lehet a külső x, //vagy az obj.x, attól függően, //hogy az obj milyen objektum x; }

A másik, hogy strict módban az eval-ban bevezetett változók kívülről nem lesznek elérhetőek. Normál módban igen, ezért az eval utáni kódban nem lehet tudni, hogy a változót elrejti-e egy ilyen új változó.

var x = 17; var evalX = eval("'use strict'; var x = 42; x"); assert(x === 17); assert(evalX === 42);

Ezzel kapcsolatos, hogy ha strict módban hívjuk meg az eval(...) függvényt, ez a kód is strict módban fog kiértékelődni. A mód explicit deklarációja is megengedett, de szükségtelen.

function strict1(str) { "use strict"; return eval(str); // str strict módú lesz } function strict2(f, str) { "use strict"; return f(str); // nem eval(...): str akkor és csak akkor lesz strict, ha deklarálja azt } function nonstrict(str) { return eval(str); // str akkor és csak akkor lesz strict, ha deklarálja azt } strict1("'Strict mode code!'"); strict1("'use strict'; 'Strict mode code!'"); strict2(eval, "'Non-strict code.'"); strict2(eval, "'use strict'; 'Strict mode code!'"); nonstrict("'Non-strict code.'"); nonstrict("'use strict'; 'Strict mode code!'");

A harmadik, hogy a strict mód tiltja egyszerű nevek törlését.

"use strict"; eval("var x; delete x;"); // !!! syntax error

Az eval és az arguments egyszerűsítése

Strict módban az eval és az arguments kevésbé furcsán viselkedik. Ezen kívül a kulcsszavakhoz hasonlóan kezeli őket, bár valódi kulcsszavak csak a későbbi ECMAScript verziókban lesznek.

Az eval és az arguments neveket nem lehet átdefiniálni. A következők mindegyike szintaktikai hiba:

"use strict"; eval = 17; arguments++; ++eval; var obj = { set p(arguments) { } }; var eval; try { } catch (arguments) { } function x(eval) { } function arguments() { } var y = function eval() { }; var f = new Function("arguments", "'use strict'; return 17;");

Normál kódban, ha egy függvény argumentuma arg, az arguments[0] és az arg egymás aliasa lesz: az egyiket megváltoztatva a másik is megváltozik. Strict módban ezzel szemben az arguments tömb a híváskori paraméter értékeket tárolja, és az egyik megváltoztatása nincs hatással a másikra.

function f(a) { "use strict"; a = 42; return [a, arguments[0]]; } var pair = f(17); assert(pair[0] === 42); assert(pair[1] === 17);

Az arguments.callee nem támogatott. Normál kódban a befoglaló függvényre mutat, de ez a hatás elérhető egyszerűen a befoglaló függvény elnevezésével, és a megléte kizár bizonyos optimalizálási lehetőségeket (például függvények inline-olása). A callee attribútum strict módban is létezik, de minden elérési kísérlet kivételt okoz.

"use strict"; var f = function() { return arguments.callee; }; f(); // TypeError-t dob

Biztonságos JavaScript

A strict mód megkönnyíti a biztonságos JavaScript kód írását. Egyes oldalak lehetővé teszik a felhasználónak, hogy a olyan JavaScript kódot írjanak, ami aztán más felhasználók nevében fog lefutni. Mivel a böngészőben futtatott JavaScript hozzáférhet a felhasználó személyes adataihoz, ezeket a szkripteket át kell alakítani futtatás előtt. Ezek közül az átalakítások néhány rengeteg futás idejű ellenőrzést igényel, és szinte lehetetlen elvégezni a hatékonyság jelentős romlása nélkül. Ha megköveteljük a felhasználótól, hogy strict kódot írjon, ezeknek a száma jelentősen csökkenthető, a következő szigorítások miatt.

A függvényeknek adott this érték nem csomagolódik autómatikusan. Egy normál függvényben a this mindig függvény: ha a megadott érték objektum, akkor az érték, ha Boolean, string vagy szám, akkor az érték becsomagolva egy objektumba, ha undefined vagy null akkor a globális objektum. (A call, apply vagy bind függvényekkel explicit megadható a this.) A globlális objektum elérése ezeknél a felhasználói szkripteknél biztonsági kockázatot jelent, ezért strict módban a this mindig változatlanul adódik át.

"use strict"; function fun() { return this; } assert(fun() === undefined); assert(fun.call(2) === 2); assert(fun.apply(null) === null); assert(fun.call(undefined) === undefined); assert(fun.bind(true)() === true);

A normál módú ECMAScript gyakran implementált kiterjesztései lehetőséget adnak a JavaScript verem bejárására. Ezekben a kiterjesztésekben a fun függvény hívása közben a fun.caller az a függvény, ami legutóbb meghívta a fun-t, és a fun.arguments tartalmazza ennek a hívásnak az argumentumait. Ezekkel a felhasználó hozzáférhet a biztonságos környezeten kívüli függvényekhez, ezért strict módban használatuk esetén kivétel váltódik ki.

function restricted() { "use strict"; restricted.caller; // TypeError-t dob restricted.arguments; // TypeError-t dob } function privilegedInvoker() { return restricted(); } privilegedInvoker();

Strict módban az arguments-en keresztül nem érhetőek el a függvényhívás változói. Régebbi implementációkban az arguments.caller objektum attribútumai a függvény változóinak aliasai voltak. Ez lehetetlenné tette a változók elrejtését függvényabsztrakció segítségével, és az optimalizálást is nehezítette. A legtöbb modern böngésző nem implementálja, de történeti okokból a strict függvényekben használata kivételhez vezet.

"use strict"; function fun(a, b) { "use strict"; var v = 12; return arguments.caller; // TypeError-t dob } fun(1, 2); // nem fedi fel a v-t (sem a-t vagy b-t)

Későbbi ECMAScript verziók előkészítése

A jövőbeli ECMAScript verziók valószínűleg új nyelvi elemeket fognak bevezetni, és a strict mód igyekszik megkönnyíteni ez átállást ezekre a verziókra. Néhány várható új szintaktikai elem használatát tiltja, ezért könnyebb lesz ezeket bevezetni a jövőben.

Néhány név kulcsszóvá válik. Ezek az implements, interface, let, package, private, protected, public, static és a yield. Ilyen nevű változókat vagy argumentumokat nem lehet deklarálni strict módban.

function package(protected) // !!! { "use strict"; var implements; // !!! interface: // !!! while (true) { break interface; // !!! } function private() { } // !!! }

Tilos ezen kívül a függvény vagy szkript legfelső szintjén kívül függvény utasítást elhelyezni. Normál, böngészőben futó kódban bárhol lehet ilyen utasítás. Ez nem része sem az ES5 sem az ES3 szabványnak, és az implementációk inkompatibilisek is. A következő ECMAScript verziók várhatóan új szemantikát vezetnek majd be ezekhez az utasításokhoz.

"use strict"; if (true) { function f { } // !!! syntax error f(); } for (var i = 0; i < 5; i++) { function f2() { } // !!! syntax error f2(); } function baz() // helyes { function eit() { } // ez is helyes } function package(protected) // !!! {