ECMAScript 5.1

ECMAScript 5.1 (ES5.1) je poslední verzí ECMAScriptu, standardu, na kterém je postaven JavaScript. Podobně jako u HTML5 je i ES5 standardizací existujících implementací a rozšíření jazyka. Jednou z nich je například strict mode, o němž jsme už psali. V článku si představíme některé další novinky verze 5.1.
Nálepky:
Překlad článku Introducing ECMAScript 5.1 (autor: Mike Taylor) z webu Dev.Opera. Překlad vychází s laskavým svolením Opera Software.
Úplný seznam novinek naleznete v přílohách D a E v oficiální specifikaci ECMAScriptu (PDF) na webu http://www.ecmascript.org/; v HTML podobě jej najdete na stránkách Michael[tm] Smith’s unofficial annotated HTML version.
Podpora ES5.1 v prohlížečích
S vydáním Opery 11.60 je ES5 podporován ve všech pěti hlavních prohlížečích. Pokud nebude řečeno jinak, budou všechny dále zmiňované vlastnosti dostupné v těchto verzích (a vyšších):
- Opera 11.60
- Internet Explorer 9*
- Firefox 4
- Safari 5.1**
- Chrome 13
* IE9 zatím nepodporuje strict mode, až ve verzi IE10.
** V Safari 5.1 stále není podpora pro Function.prototype.bind
, ačkoli před nedávnem byla přidána do Webkitu
.
Stav podpory ve starších prohlížečích naleznete na stránce ECMAScript 5 compatibility table od Jurije Zajceva (Juriy Zaytsev).
Strict Mode v ES5
Strict mode (viz ECMAScript Strict mode ve Firefoxu 4) je způsob, jakým se vývojář může přihlásit k restriktivnější verzi jazyka, která přináší vyšší bezpečnost a spolehlivost. Strict mode je nastaven přidáním direktivy "use
na začátek souboru nebo funkce. Protože je tato direktiva prostý řetězec, bude bez problémů staršími prohlížeči ignorována.
strict";
"use strict";
function strict(){ "use strict"; //... } function sloppy(){ eval("window.foo = 'bar'"); }
Mnoho věcí, které v normálním JavaScriptu způsobí nepředvídatelné chybné chování, způsobí ve strict mode výjimku, kupříkladu:
- Přiřazení do nedeklarované proměnné vyhodí
ReferenceError
(v prostém JS vytvoří globální proměnnou). - Vícenásobné přiřazení do stejně pojmenované vlastnosti v objektovém zápisu vyvolá
SyntaxError
. - Byla odstraněna konstrukce
with
. Její použití vyvoláSyntaxError
.
Pěkné shrnutí těchto rozdílů naleznete v článku na MDSN.
JSON
ES5 zavádí globální objekt JSON
, který slouží k serializaci ( JSON.stringify
) a deserializaci ( JSON.parse
) objektů do/z formátu JSON.
U starších prohlížečů lze použít polyfill skript json2.js od Douglase Crockforda, který poskytuje stejnou funkčnost (samosebou nejprve otestuje nativní podporu).
JSON.parse(text [, reviver])
JSON.parse
bere řetězec (ve formátu JSON) a vrací hodnotu. Volitelný parametr reviver je funkce s dvěma argumenty, key
a value
, která je provedena nad výsledky, což je umožňuje filtrovat a měnit před tím, než jsou vráceny.
>> var result = JSON.parse('{"a": 1, "b": "2"}'); Object >> result.b "2"
Pokud se chceme ujistit, že výsledkem bude celé číslo (nikoli řetězec), použijeme tuto funkci.
var result = JSON.parse('{"a": 1, "b": "2"}', function(key, value){ if (typeof value == 'string'){ return parseInt(value); } else { return value; } }) >> result.b 2
JSON.stringify(value [, replacer [, space]])
JSON.stringify
bere hodnotu a převádí ji na řetězec ve formátu JSON. V nejjednodušší podobě přebírá jednu hodnotu a vrací řetězec.
>>> var mike = JSON.stringify({mike: "taylor"}) undefined >> mike '{"mike": "taylor"}' >> typeof mike "string"
Když potřebujeme změnit způsob, jakým je hodnota převedena na řetězec, nebo filtrovat data, můžeme předat funkci replacer
. Kupříkladu pokud chceme vynechat hodnotu 13, můžeme to provést takto:
var nums = { "first": 7, "second": 14, "third": 13 } var luckyNums = JSON.stringify(nums, function(key, value){ if (value == 13) { return undefined; } else { return value; } }); >> luckyNums '{"first": 7, "second": 14}'
Pokud funkce replacer vrátí hodnotu undefined
, nebude tento klíč ve výsledném JSON zahrnut. Třetím argumentem je počet mezer, kterým se má odsadit zanoření (kvůli lepší čitelnosti výsledného textu), nebo řetězec, který je přidán jako výplň odsazování. Hodnoty větší než 10 jsou převedeny na 10, řetězec delší než 10 znaků je oříznut a je použito pouze prvních deset znaků.
var luckyNums = JSON.stringify(nums, function(key, value) { if (value == 13) { return undefined; } else { return value; } }, 2); >> luckyNums '{ "first":7, "second":14 }'
Rozšíření Object
Ke konstruktoru Object
byly přidány následující metody:
Object.getPrototypeOf
Object.getOwnPropertyDescriptor
Object.getOwnPropertyNames
Object.create
Object.defineProperty
Object.defineProperties
Object.seal
Object.freeze
Object.preventExtensions
Object.isSealed
Object.isFrozen
Object.isExtensible
Object.keys
Jednou z výhod je lepší řízení práce s vlastnostmi objektu, např. určení, které lze měnit, procházet, mazat apod. Tyto možnosti se nastavují pomocí tzv. property deskriptorů. Kupříkladu:
var cat = {}; Object.defineProperty(cat, "name", { value: "Maru", writable: false, enumerable: true, configurable: false }); Object.defineProperty(cat, "skill", { value: "exploring boxes", writable: true, enumerable: true, configurable: true });
U našeho objektu cat
nelze měnit vlastnost name
, ale objeví se při procházení smyčkou for-in
(enumerable). Kromě jiného je Maru dobrá v prozkoumávání krabiček, ale tato schopnost může být v budoucnu změněná, tak má vlastnost skill
ponechané příznaky writable
a configurable
.
V některém z dalších článků se k těmto vlastnostem vrátíme.
Vylepšení polí
K prototypu Array
byly přidány následující možnosti:
Array.prototype.indexOf
Array.prototype.lastIndexOf
Array.prototype.every
Array.prototype.some
Array.prototype.forEach
Array.prototype.map
Array.prototype.filter
Array.prototype.reduce
Array.prototype.reduceRight
Dmitrij Šošnikov (Dmitry Soshnikov) napsal podrobný článek o těchto rozšířeních polí v ES5.
Jedna metoda, kterou Dmitrij v článku nezmínil, je Array.isArray
, která je přidána přímo ke konstruktoru, nikoli k prototypu. Array.isArray
dělá to, co od něj očekáváme – je to metoda, která vrátí true
nebo false
podle toho, jestli vnitřní vlastnost [[Class]]
je rovna „Array“.
Array.isArray("NO U") >> false Array.isArray(["NO", "U"]) >> true
V ES3 byl jediný použitelný způsob, jak určit, zda je hodnota pole nebo ne, takzvaný „the Miller Device“, neboli porovnání interní vlastnosti [[Class]]
s tou, kterou má pole.
Object.prototype.toString.apply(value) === '[object Array]'
Function.prototype.bind(thisArg [, arg1 [, arg2, …]])
Function.prototype.bind
vrací novou funkci, u níž je hodnota this nastavena podle argumentu thisArg
. To umožňuje vykonat funkci v kontextu jiného objektu
function locate(){ console.log(this.location); } function Maru(location){ this.location = location; } var kitty = new Maru("cardboard box"); var locateMaru = locate.bind(kitty); locateMaru();
V tomto příkladu voláme funkci location
v kontextu objektu Maru. Protože locate
je vlastností globálního objektu, je v něm this
rovno právě globálnímu objektu ( window
). V našem případě hledáme kočku, nikoli objekt Location
, takže si vytvoříme novou funkci locateMaru
, kde je hodnota this
svázána s kitty
.
Další zdroje informací
- ECMAScript 5 Objects and Properties John Resig
- Understanding JavaScript Function Invocation and “this” Yehuda Katz
- JavaScript Strict Mode Angus Croll
- ECMA-262–5 in detail: Introduction Dmitry Soshnikov
- ECMAScript 5 compatibility table Juriy Zaytsev
Vdaka za clanok. V kombinacii s dojotoolkit-om a html5 mame uz v rukach solidne standardy na strane weboveho klienta. Este implementovat niektore dolezite akcie nad img: hlavne vyrez.
Jen dotaz, odkdy je HTML5 standardem?
Že by mi něco ušlo a HTML5 by měl svůj závazný jasně určený betonově stabilní standard?
Považuji HTML5 do doby, než vyjde závazný a konečný popis hotového standardu za cokoli jen ne za standard.
JavaScriptu dost citelně (a nepochopitelně) chybí nativní funkce pro vytvoření mělké kopie objektu. Kopírování přes výčet vlastností je šíleně pomalé.
Kolikrat do roka takovou kopii potrebujes? Jinak samozrejme ty funkce tam jsou, ale jmenuji se jinak ;)
function copy(objToCopy) {
return JSON.parse(JSON.stringify(objToCopy));
}
To je potřeba v jednom kuse :-) Tahle operace je přeci pro prototypový jazyk naprostý základ. To se pak člověk nestačí divit, když nahodí profiler a zjistí, že mu taková blbost, jako kopírování objektů zabere třetinu času :-) Dnes je nejrychlejší si vytvořit funkci, ve které se ručně přehází všechny prvky objektu.
Nehledě na to, že z hlediska zapouzdření by se měla (obecná) kopie objektu dělat tak, že si objekt vytvoří svoji mělkou kopii a té se pak zavolá něco jako postCopy, čímž si kopie objektu sama vytvoří hlubší kopii vybraných prvků bez toho, aby mezi původním objektem a jeho kopií musela probíhat nějaká další komunikace.
Pěkný řešení, ale nebude fungovat všude. Tohle je z hlediska kompatibility o něco lepší trik:
no, nápad dobrej, ale ještě by to chtělo asi uzavřít do closure:
Stejně by mě ale zajímalo, jak tohle bude fungovat; prototype.prototype se určitě procházet nebude, takže to stejně asi není (dobré) řešení na všechno
Tvoje verze je o něco elegantnější, ale náročná na paměť, která se bude ukusovat s každým voláním. Původní verze by šla přepsat tak, že funkce copy bude používat sama sebe:
Když už jsme u toho ES5, nešlo by jednoduše použít tohle:
Tak jako tak ale může vzniknout problém s hasOwnProperty na tomto novém objektu.
Ne, tato funkce vytváří objekt s daným prototypem a vlastnostmi, nikoliv jeho kopii.
var o1 = {a: 1, b: 2};
var o2 = Object.create(o1);
o1.a = 3;
alert(o2.a) // vysledek je 3
Tato funkce nevytváří skutečnou mělkou kopii objektu, používat ji za tímto účelem může vést k velmi zapeklitým chybám.
To je vlastně pravda. Ještě by to šlo takhle :-)
o1 = {a: 1, b: 2};
o2 = copy(o1);
o1 = copy(o1);
o1.a = 3;
alert(o2.a); // == 1