Co se mi nelíbí na JavaScriptu

Když jsem v 90. letech s JavaScriptem začínal, tak jsem ten jazyk nesnášel. Hlavní důvod byl ale ten, že co jsem napsal pro jeden prohlížeč, nefungovalo v druhém. Když jsem pochopil, že to není až tak problém jazyka, ale rozdílného API v prohlížečích, tak jsem si JavaScript postupně začal oblibovat, až se stal mým druhým nejoblíbenějším jazykem. Navíc je mnohem elegantněji navržený než PHP a temných zákoutí je v něm mnohem míň.
Na JavaScriptu se mi nejvíc líbí myšlenka, že funkce je úplně obyčejná hodnota podobně jako třeba řetězec – dá se kdekoliv definovat, předávat, není na ní zkrátka nic zvláštního. Většina ostatních jazyků, které znám, považuje funkce za něco speciálního a teprve v poslední době umožňuje funkce vytvářet a používat na místech, kde se dá pracovat s jakýmikoliv jinými hodnotami.
Část věcí, které se mi na JavaScriptu nelíbí, je nejspíš dána tím, že byl navržen za 10 dní před téměř 20 lety a od té doby se prakticky nezměnil. Část je ale také dost možná jiná filozofie.
Nutnost psát var
Pro vytvoření lokální proměnné je potřeba použít klíčové slovo var
. Na tom by nebylo nic až tak špatného, pokud by jeho neuvedení neznamenalo, že pracujete s globální proměnnou. Chování v PHP, kde se defaultně pracuje s lokálními proměnnými a pokud chcete globální, tak musíte použít global
, mi přijde mnohem lepší.
Řekl bych, že to je do značné míry vynuceno vlastností, že funkce jsou normální hodnoty. Když v JavaScriptu voláme funkci f
, musí se najít proměnná f
a zkusit se zavolat hodnota, která je v ní uložená. Pokud by práce s globálními proměnnými byla explicitní, tak bych vždy musel uvést, že chci volat globální f
a ne tu lokální.
Function scope proměnných
Block scope je mnohem přirozenější a příjemnější na používání než function scope, kromě toho vede k méně chybám. Navíc v JavaScriptu všechny lokální proměnné vzniknou hned na začátku funkce, takže následující kód je platný:
function () {
alert(a); // Zobrazí undefined
var a = 'Ahoj';
}
Pokud navíc v jedné funkci stejnou proměnnou deklarujete vícekrát (např. dva cykly ve tvaru for (var i = 0; ; )
), tak vám nástroje jako JSHint vyhubují. Abyste se tomu vyhnuli a abyste zdůraznili okamžik vzniku proměnných, bylo by nejlepší všechna var
napsat hned na začátek funkce, to je ale pořádný opruz.
ECMAScript 6 zavádí block scope pomocí klíčového slova let, v prohlížečích ho ale stejně nepůjde dalších několik let používat kvůli kompatibilitě.
Iterace polí
Nejspolehlivější způsob iterace polí je pomocí for
cyklu procházejícího od nuly do array.length
. Je to zbytečně krkolomné, index prvku mě často vůbec nezajímá, počet prvků taky zjišťovat nechci. Navíc k iterovanému prvku musím přistupovat přes pole a aktuální index. Pokud chci navíc procházet výsledek funkce, musím si ho uložit do dočasné proměnné. Kód pak vypadá nějak takhle:
var tags = getTags();
for (var i = 0; i < tags.length; i++) {
var tag = tags[i];
// tady bude kód pracující s tag
}
Oč jednodušší to je v PHP:
foreach (getTags() as $tag) {
// tady bude kód pracující s $tag
}
V ECMAScript 5.1 jde používat array.forEach, ale ani to se mi kvůli API používajícímu callback nezdá jako nejlepší řešení.
Iterace objektů
Iteraci objektů taky nepovažuji za ideální především proto, že se prochází i uživatelem definované vlastnosti na prototypu. Takže pokud nějaký chytrák definuje třeba Object.prototype.clone
, tak bude clone
strašit při iteraci všech objektů. Jako obranu byste ve všech iteracích měli používat hasOwnProperty:
for (var key in map) {
if (map.hasOwnProperty(key)) {
var value = map[key];
// tady bude kód pracující s value
}
}
Sám jsem naštěstí závislost na cizím kódu hackujícím Object.prototype řešit nemusel, takže hasOwnProperty nepoužívám a problém mě tolik netrápí. I když iterace výhradně přes klíče a nutnost hodnotu získat mě taky obtěžuje. Oč jednodušší je PHP verze:
foreach ($map as $value) {
// tady bude kód pracující s $value
}
Objekt je HashMap, nikoliv LinkedHashMap
Pole v PHP dovoluje přistoupit k prvku podle klíče v konstantním čase a zároveň ručí za pořadí při procházení. V Java terminologii jde o LinkedHashMap. JavaScript kupodivu za pořadí při procházení objektu neručí (jde tedy o HashMap), i když ho prohlížeče až na některé okrajové případy dodržují. Donedávna jsem o téhle záludnosti neměl tušení. Pokud potřebujete pořadí dodržet, můžete si klíče ukládat do pole, které budete používat při iteraci.
Čárka za posledním prvkem
Za posledním prvkem pole nelze psát čárku. Tedy – už ECMAScript 3 to umožňuje, ale Internet Explorer to měl špatně, takže [0,].length
všude vrací 1, ale IE<9 vrací 2. Za posledním prvkem objektu jde psát čárku až od ECMAScript 5, kvůli kompatibilnímu režimu prohlížečů to tedy půjde univerzálně používat až za několik let.
Tento nedostatek vadí především u diffů větších polí a objektů formátovaných na více řádek. Kdykoliv na tohle narazím, tak si představuji, jak to mohlo vypadat v květnu 1995: „Hele, parser nějak funguje, zítra to odevzdávám, trailing comma tam doplním v další verzi.“
Nepovinný středník
Automatické vkládání středníku na nahodilá místa také nepovažuji za právě povedenou vlastnost. Co myslíte, že vrátí následující funkce?
function () {
return
{
x: 2
}
}
Uhodli jste, že undefined
? Za return
si totiž JavaScript domyslí středník a následující objekt pochopí jako blok s návěštím x
a kódem 2
(za kterým si taky domyslí středník).
V praxi se s tímto problémem člověk naštěstí často nesetká, dnes a denně na něj ale narážím prostřednictvím Google JavaScript Style Guide, který kvůli vkládání středníků vyžaduje zalamování dlouhých řádků za operátorem, nikoliv před ním (jak jsem na to zvyklý jinde).
Prototypová dědičnost
Nápad dědit z vytvořených objektů místo ze tříd jsem nikdy nevzal za své. Když už pominu paměť zbytečně alokovanou při vytváření objektu přiřazovaného do prototypu potomka, jak asi nastavím parametry konstruktoru předka, když ty budu znát až v konstruktoru potomka, aha? ECMAScript 5.1 dovoluje vytvořit prázdný objekt předka pomocí Object.create, sám používám goog.inherits nebo obdobu, což do prototypu přiřadí prázdný objekt, jehož prototyp nastaví na prototyp předka.
Přepisování metod objektu
Hrůzou mi vstávají vlasy na hlavě při představě, že zavolám nějakou cizí funkci, a ona mi pod rukama změní metody mého objektu – třeba je vymění za něco jiného. V testech se to může hodit, ale v běžném životě to nepřináší nic dobrého. Navíc nejsem nijak chráněn proti překlepům – když něco přiřadím do this.itme
místo this.time
, tak se o tom nijak nedozvím.
ECMAScript 5.1 nabízí řešení v podobě Object.freeze a Object.seal, to je ale poměrně krkolomné na používání:
function Square(x) {
this.x = x;
}
Square.prototype = Object.freeze({
getArea: function () {
return this.x * this.x;
}
});
var square = Object.seal(new Square(2));
console.log(square.getArea());
square.x = 3;
console.log(square.getArea());
Object.freeze způsobí, že vlastnosti nejde přidávat, měnit ani mazat – to chceme typicky u prototypu. Object.seal způsobí, že vlastnosti nejde přidávat a mazat, pořád jdou ale měnit – to chceme typicky u vlastností definovaných v konstruktoru.
Předávání this
Funkce jako first-class citizen miluji, dojem ale poněkud kazí chování this
. Jde o to, že když někam předáte funkci, tak její this
bude nějaký jiný, typicky globální objekt. To je potřeba zcela ojediněle a případně se to dá triviálně vyřešit předáním onoho objektu jako parametru funkce. Lépe by mi vyhovovalo, když by this
bylo nastaveno na objekt, přes který jsme k metodě přistoupili, případně na aktuální this
uvnitř closure. V praxi je pak kód zaset spoustou zbytečných Function.bind, které jen nastavují this
na this
.
S tímto nedostatkem souvisí i to, že vaší metodě může někdo nastavit this
na libovolný objekt.
Dvě prázdné hodnoty
V JavaScriptu jsou podle mě úplně zbytečně dvě různé hodnoty, které znamenají „nic“ – null
a undefined
. Obě bohužel potřebujete, protože undefined
mají proměnné, dokud do nich nic nepřiřadíte, a null
vrací některé funkce, např. prompt. V Closure anotacích pak lze najít nádhery jako /** @type {?number|undefined} */
, kde ?
znamená null
a undefined
znamená undefined
.
Ostatní jazyky, které používám, si vystačí s jednou prázdnou hodnotou. I když chápu akademický smysl dvou různých prázdných hodnot, tak v praxi je to k ničemu a jen to otravuje život.
Chybějící operátor list
Občas mi chybí operátor list
dovolující přiřadit více proměnných najednou. Ale je to vlastně jen po volání regExp.exec
. Většinou absence této konstrukce vede jen k tomu, že funkce vracející více hodnot vrací objekty a ne pole, což je přehlednější. V ECMAScript 6 to řeší destructuring assignment.
Řetězce v apostrofech nebo uvozovkách
Dřív mi vyhovovalo, že hodnoty atributů v HTML, řetězce v PHP, řetězce v JavaScriptu a dokonce i řetězce v MySQL se dají uzavírat jak do uvozovek, tak do apostrofů. Časem mi ale začala nejednotnost vadit víc než to, že občas ušetřím pár zpětných lomítek. V PHP aspoň uvozovky a apostrofy znamenají dvě různé věci, v JavaScriptu bych uvozovky využil na něco jiného – ideálně na regulární výrazy, jejichž uzavírání do lomítek ztěžuje takové to domácí parsování JavaScriptového kódu.
V praxi na kód s uvozovkami naštěstí díky coding style nenarážím.
Callback hell
Krása konceptu předávání funkcí zavolaných po dokončení asynchronní operace se bohužel poněkud rozmělňuje nutností do sebe tyto callbacky hluboko zanořovat. Vede to ke kódu mnohem nepřehlednějšímu, než když je psaný sekvenčně. Sám na to naštěstí tolik nenarážím, protože na klientské straně na sebe asynchronní operace většinou moc často nenavazují. Ale je to jeden z důvodů, proč mě příliš neláká node.js.
Ve Facebooku se dal asynchronní kód psát sekvenčně díky operátoru yield, který bude i v ECMAScriptu 6. Osobně by se mi ale ještě víc líbilo async/await, což je ale hudba vzdálené budoucnosti, pokud vůbec nějaké.
Závěr
JavaScript považuji za dobrý jazyk. Kromě zmíněných hodnot typu funkce se mi líbí třeba i objektové literály nebo fakt, že kód běží v jednom vlákně, i když je celkem běžně asynchronní – kód a uvažování o jeho chování to dramaticky zjednodušuje a žádné výrazné problémy to nezpůsobuje. Perfektní je i to, že ||
vrátí první truthy prvek. Vím, že některé nedostatky by mi pomohl překlenout CoffeeScript nebo jiný transpiler, na JavaScriptu mi ale vyhovuje hustota informací ve zdrojovém kódu, kterou považuji u CoffeeScriptu za příliš vysokou. Ze stejného důvodu jsem si nikdy neoblíbil Perl – přišel mi příliš nepřehledný, zato kód v PHP jsem chápal, aniž bych se jazyk musel učit. Java je pro mě zase příliš řídká. S některými věcmi pomáhá Closure Library a Closure Compiler, ale milejší by mi bylo, když bych na nich nemusel záviset. TypeScript mi ve srovnání s možnostmi ekosystému Closure přijde jako chudý příbuzný.
Sérié článků „Co se mi nelíbí“ na Javě, na Go, na JavaScriptu nasvědčuje tomu, že hledám jazyk, ve kterém by se mi programovalo stejně pohodlně jako v PHP (nebo pohodlněji). JavaScript tím jazykem, který bych bez přemýšlení použil na cokoliv, bohužel také není.
V době, kdy jsem se po 10 letech v PHP rozhodl, že TEĎ se konečně do toho JavaScriptu pořádně ponořím, je to trochu demotivujíci čtení a nezbývá než doufat, že následné komentáře javascriptařů, v které doufám, mému, už tak těžce zkoušenému elánu, pomůžou jít dál. Každopádně, z mého pohledu, výborný text od autora, za kterým něco je, díky.
Já jsem se po 11 letech strávených s PHP a JS rozhodl vyzkoušet Python a Ruby. Nakonec jsem zůstal u Pythonu a jsem rád, že nemusím řešit žádná „temná zákoutí“ jazyka. Tak bych doporučoval porozhlédnout se i jinde. Pokud ale zvítězí JS, tak doporučuji rovnou CoffeeScript, který tě odstíní od temných zákoutí JS.
To proto, že ho neznáš dost dobře. Viz podnadpis Temné kouty: Python poznámky.
S JS klidně pracuj, ke každému jazyku jde napsat odstavce co je na něm špatně. Pravda u JS jsou různé podivnosti, ale stejně je to zajímavý jazyk. K PHP je JS vhodné, když se to tak často kombinuje (nemusí to tak člověk používat sám, stačí při procházení cizího kódu). Minimálně ti to rozšíří obzory, čím víc jazyků a knihoven, tím lehčeji se učí další.
aky mas nazor na hack? http://hacklang.org/
rsp ine pokusy spravit php strongly typed
a co celkova idea HHVM?
Také jsem hledal… A našel. Dart. Vlastně řeší 90% nedostatků vypsaných v tomto článku a ještě není syntaxí příliš vzdálený od původního JavaScriptu a je přehledný jako PHP. Spokojenost :-)
dart je skor java ako php
Tak samozrejme, ze syntaxi se blizi Jave (mnoho lidi ma kvuli tomu vuci Dartu predsudky), ovsem bere si z Javy jenom to nejlepsi. Ale kombinace PHP a Dartu je treba pro me hodne dobra
Jak Jakub sám říká, spoustu nedostatků odstraňuje CoffeeScript – odstiňuje od prototypové dědičnosti, iterování přes pole a objekty je triviální, velmi mi rovněž vyhovuje klíčové slovo
when
. Funkce pro nalezení objektu v poli např podle id se pak dá napsat elegantně na jeden řádek:findUserById: (id) -> return user for user in users when user.id is id
Zásadní však pro mě bylo to, že jsem po letech chtěl vyzkoušet jinou než C-like syntaxi.
Poslední poznámka – callback hell se dá částečně vyřešit použitím návrhového vzoru promise.
Doplnil bych, že Objekt v JS není dokonce ani HashMap – je to jen prostá neuspořádaná množina klíčů a hodnot. Specifikace o tomto pokud vím mlčí, nicméně dle mých informací objekt v žádné mainstreamové implementaci nedovoluje přístup v (amortizovaném) konstantním čase. Samotného by mne zajímalo proč a jestli nás v tomto směru čeká nějaké zlepšení. Možná ES6 [Weak]Map?
Dále bych rád znal autorův názor ohledně funkcionální iterace, která se mu kvůli callbacku nelíbí. Druhým parametrem forEach a dalších je přitom „this“, takže odpadá nutnost vizálně nehezkého explicitního bindu (viz jeden z dalších popisovaných nedostatků). Jaké problémy tedy u této iterace zůstávají?
Vposled bych prosil o rozvedení věty „Když už pominu paměť zbytečně alokovanou při vytváření objektu přiřazovaného do prototypu potomka“. U typizovaného zápisu „var parent = {}; var child = Object.create(parent);“ nikde žádnou zbytečně alokovanou paměť nevidím; možná se výtka týká spíše operátoru „new“ (a nezvyklé duality posloupností funkcí a jejich prototypů)? Navíc při definování vztahu předek-potomek přeci žádné konstruktory volat nemusím; to udělám až při instancializaci potomka (kdy v jeho konstruktoru za správné parametruzace volám konstruktor předka, opět korektně parametrizovaný).
Děkuji za doplnění.
Na
forEach
se mi nelíbí vlastně jen to, že přidává syntaktické smetí – nutnost psátfunction
a předávatthis
, navíc až za definicí funkce (fragment}, this);
mému oku taky zrovna nelahodí).Funkce
Object.create
nealokuje žádnou paměť? Vzhledem k tomu, že má v popisu práce vytvořit nový objekt, tak by mě to překvapilo.Poznámka o volání konstruktorů naráží na dobu před
Object.create
, kdy se vztah dal vyjádřit pomocíChild.prototype = new Parent()
, což je celkem přirozené, ale kvůli nemožnosti předat parametry konstruktoru bohužel nepoužitelné. Takže je nutno dělat v článku zmiňované opičárny s vytvářením prázdného objektu. Osobně siObject.create
ještě kvůli starým prohlížečům používat netroufnu.Jasně,
Object.create
paměť alokuje, ale neřekl bych, že zbytečně – je to hlavní náplní jeho práce a takto alokovaná paměť se následně využije. Jak v případěvar child = Object.create(parent)
(tj. používání dědičnosti bez funkcí), tak vChild.prototype = Object.create(Parent.prototype)
(při emulování tradiční class-based dědičnosti).Child.prototype = new Parent()
je zhůvěřilost, ano.Použití
Object.create
vnímám jako velmi bezpečné i u starých prohlížečů, pokud nepotřebujeme použít druhý parametr s deskriptorem. Lze buď napsat triviální polyfill s použitímnew
:Object.create = function(proto) {
var tmp = function() {};
tmp.prototype = proto;
return new tmp();
}
ve kterém ale skutečně dochází ke zbytečné alkoaci (ale koho to u polyfillu trápí?), nebo využít zajímavý trik zmiňovaný na konci článku na https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create – takovýto polyfill dle mého názoru nic zbytečně nealokuje a přesto funguje všude.
To o tom JS objektu by mě zajímalo. Já to nikdy nehledal a předpokládal jsem, že je to hash mapa. I když jsem si dělal nějaké rychlostní srovnání, tak dotazy na existenci klíče v hash mapě vycházely řádově rychleji než prosté lineární vyhledání prvku v seznamu. Jak je to možné, když má být objekt neuspořádaná množina klíčů a hodnot?
Nu, já se prostě optal vývojářů SpiderMonkey a V8 a dle jejich odpovědí to hashmapa nebyla. To ale neznamená, že se při přístupu ke klíči musí dělat „full scan“ (lineární vyhledání), klíče mohou být třeba v nějakém stromu, který vede na lepší (logaritmickou) složitost. První verze V8 například při iteraci klíče vracely v abecedním pořadí, nezávisle na pořadí vkládání (to dává tušit leccos o implementaci). Časem bylo toto chování tuším upraveno tak, aby korespondovalo ostatními implementacemi (přestože standard o pořadí iterace mlčí). A možná ještě ne: https://code.google.com/p/v8/issues/detail?id=164
Strom už by to teoreticky mohl být, ale z toho popisu „je to jen prostá neuspořádaná množina klíčů a hodnot“ jsem myslel, že to není ani to.
A kde jste se jich ptal? Mě by tohle docela zajímalo, sám jsem o tom moc nenašel. Ani v tom odkázaném vláknu toho zase tak moc užitečného nepíší. Pořadí klíčů může být uloženo bokem, to taky není nijak pevné vodítko.
Stačí se podívat do zdrojáků ne?
Tak třeba V8 se zdá property prochází lineárně, ale koukal jsem do toho asi jen 10 sekund:
https://github.com/v8/v8/blob/master/src/objects.cc#L111
Za V8 mám vynikající zkušenost s Vyacheslavem Egorovem (nyní už tuším dělá Dart VM), přímo (ptal jsem se ho buď přes jabber, nebo naživo).
Za SM jsem to s někým diskutoval – ale už je to nějaká doba zpátky – na konferenci jsconf.eu. Jméno bohužel nevím, ale jak psal Jirka Kosek: zdrojáky jsou veřejné a to by mělo stačit :-)
V ES6 se dají použít arrow funkce, které this zachovávají.
Jojo, zvlášť co se týká prototypové „dědičnosti“ a chování this si říkám, že autoři museli být zamilovaní do Cimrmana a jeho pokusů s vyfukováním tabákového kouře do vody.
Netvrdím, že je Python vhodný na stejné aplikace jako Javascript nebo PHP, ale má také velmi širokou škálu možných použití – webové aplikace nevyjímaje – je dobře čitelný a rychlý.
to urco, akorat me teda mrzi, ze se nejak vyrazneji neprosazuje ve webove sfere. to je v podstate primarni duvod, proc jsem se do nej nezadloubal vic, a porad holt vevodi Javascript i pres vyse uvedene nedostatky.
Server-side python docela frci, rozhodne vic nez JavaScript. Ale tys zrejme myslel browser-side. Tam bohuzel python neni k dispozici, tedy ne ze by to nezvladl, ale bohuzel nema podporu ze strany vendoru. Ano jsu tu snahy o prekladac Py->JS ( Skulpt, ale je to ve stadiu experimentu.
ad Skulpt: neni to prekladac do JS, ale interpretr (casti) pythonu.
Brython je na tom docela dobře. Já ho třeba pro svoje amatérské věci používám rozhodně radši, než javascript.
Ještě existuje PyJS neboli PyJamas. Ale osobně mi přijde, že každý Python programátor se v CoffeeScriptu zorientuje docela rychle.
Ona ta čárka na konci má totiž v js geniální (sarkazmus) význam. V JS totiž lze napsat a = [,,,], a ono si to mezi čárky doplní Undefined. Z toho logicky plyne, že za tu poslední čárku si taky musí doplnit undefined. No, mě bylo dost líto, že ten chlap už není šéf mozilly, protože takhle je nebezpečí, že se bude mít čas a něco zase vymyslí,
Zdravím,
čárka za posledním prvkem pole nebo objektu je zlo. Čárka má oddělovat jednotlivé prvky pole, takže když vidím čárku očekávám pokračování dalším prvkem. Zejména v kombinaci se špatným odsazováním je to spolehlivý způsob jak vytvořit zmatený kód.
Souhlasím, přijde mi to dost zvláštní. Překvapilo mě, že v node js coding guide jsem to zahlédl (snad se teď nepletu) jako ukázku „jak by se to mělo dělat“. S čárkou nakonci. Pamatuji si, že staré verze IE na tomhle v js padaly.
Chybějící čárka za posledním prvkem je problém u tohoto formátování kódu:
Když chci přidat další prvek, nemůžu to udělat prostým zkopírováním a úpravou posledního řádku, musím ho i upravit. Když to udělám, diff pak vypadá takhle:
Že jsem jen přidal jeden prvek, je v tom zbytečně zamlžené. Navíc to znehodnocuje blame – když chci vědět, kdo přidal řádek s
y
, tak se to prostým pohledem na daný řádek nedozvím. Stejné problémy samozřejmě nastávají, pokud chci poslední prvek naopak smazat – nestačí smazat jeden řádek, musím upravit i ten před ním.Takže tady máme tři nevýhody (pracnější úprava, složitější diff, znehodnocený blame) a jednu zdánlivou výhodu – přehlednější kód. Ale tato zdánlivá výhoda má aspoň pro mě osobně nulovou hodnotu, protože následující kód je úplně stejně přehledný:
Naprostý souhlas s Jakubem.
naprosty nesouhlas s Jakubem. Blbe se to cte. Neodpovida to „normalnim“ programovacim jazykum, ani jazykum neprogramovacim. Jestli te vazne prudi ze pak mas diff na dva radky, tak to muzes zapisovat takhle:
Kazdopadne blame je sam o sobe dost na prd. Vzdycky je potreba podivat se na diff.
Tento způsob zápisu jen přesouvá problém z posledního řádku na první. Navíc pokud se máme bavit o čitelnosti, tak tohle je naprostá tragédie.
Jaký je Váš názor na Scalu? Četl jsem všechny Vaše zmíněné články a přijde mi, že je asi nejmenší zlo. Sám ji používám už delší dobu a (po týdnu zvykání na syntaxi) je to extrémně silný a pohodlný jazyk.
Zatím jsem neměl příležitost Scalu vyzkoušet.
Scala ma spoustu prima vlastnosti, ale IMO uz je prilis slozita. Pokud jde jenom o to zbavit se bolesti Javy a pridat zakladni moznost neproceduralnich jazyku, tak bych sel do Kotlinu. Je to o neco pragmatictejsi pristup a je ocividne zamereny na moznost pokracovat tam, kde konci Java, spis nez na to napsat co nejvic paperu o language design.
Při vývoji v JS nesmíme zapomenout naučit se zpaměti tuto malou tabulku pro použití nuly :)
http://zero.milosz.ca/
Každý jazyk má své výhody a nevýhody. Platí to i pro některé špeky v PHP. Proč se v php používá např. speciální operátor „.“ na sčítaní řetězců? Už hodně krát jsem na tuhle nelogičnost naběhnul. Všechno ostatní se sčítá přes „+“, ale při pokusu sčítat řetězce z toho vyjde nesmysl.
Pokud jde o vnořování callbacku tak to není nutné psát všechny callbacky inline. Stačí místo inline definice napsat referenci na funkci. Tím dostanu lineární seznam funkcí v jednom objektu.
Oddělení operátoru pro sčítání a zřetězení naopak u dynamicky typovaného jazyka dává velmi dobrý smysl. Když uživatel do formuláře zadá třeba 20 a 15, tak
a.value + b.value
vrátí v JavaScriptu 2015. Abych dostal 35, musím napsat např.+a.value + +b.value
, což není úplně vzhledné. Naopak$_GET['a'] + $_GET['b']
v PHP funguje bez problémů. V PHP se pomocí+
sčítají čísla, nikoliv „všechno ostatní“.Reference na funkci je často ještě horší – proměnné používané ve vnější funkci si musím explicitně poslat pomocí
bind
, kód se navíc fragmentuje na často jednořádkové funkce. Takže místo toho, aby funkce byly nějaké logické celky, tak jsou to jen fragmentu kódu, které samy o sobě často nedávají žádný smysl.Tady se evidentně neshodneme. Pokud sčítám řetězce, tak chci aby to byl zase řetězec. U čísel zase čekám jako výsledek číslo.
Pokud se týká callbacků tak předávání přes parametry nebo dokonce uzávěry do inlinovaných funkcí je opravdu cesta do pekel.
Měl jsem na mysli konstrukci kdy místo callback: function(param) {tělo funkce} napíšu konstrukci callback: this.mujCallback a v tom samém objektu ve kterém je aktuální funkce nadefinuji i metodu mujCallback. Vše je lineární protože jde o dvě metody stejného objektu.
Iterace polí pomocí forEach je naopak velmi příjemná a nápomocná, přeci jen fci pro implementaci iterace lze znovupoužít pro více iterací (a mnohdy se i takto děje).
U objektu bych implicitně předpokládal chování jako HashMap nikoliv LinkedHashMap, přeci jen spoléhat se na pořadí klíču u mapy není zrovna zdravé a zavání to špatným návrhem.
Callback hell – již dávno není problém, naopak se pro asynchronní programování velmi hodí a při využití Promises se stává velmi mocným nástrojem, chainovaní promis v node.js napomáhá čistému a především neblokujicímu kódu.
Mam ted rocni zkusenost s PHP, nekolik let s JS ale spis paberkovani a zatim jsem nepotreboval vic nez jQuery.
Puvodne jsme javista, asi 7 let a ted delam prave rok s PHP a po roce prace s PHP si na tenhle jazyk nemuzu porad zvyknout.
Co me osobne vadi je vysoka nevyhoda PHP pri refaktoringu, nebo asipon nevim jak spravne refaktroing delat.
Refaktoring je pro me zakladni nutnost pri vyvoji OOP aplikaci, protoze neustale delate vyssi a vyssi abstrakce, presuny, konsolidace, proste ten objektovy model je zlvi a neustale se vylepsuje , tak jak se projekt postupne vyviji.
No a v PHP mam s timhle problem, refaktoring tam moc nefunguje. Pokud se rozhodnu pro treba zavedni nejak nove abstraknti tridy, kdy se ukaze ze proste vice objektu z ni benefituje, tak je to v PHP dost porod v podstate jen na zakalde toho, ze si neni schopny overit zachovani zakladni kompilovatelnsoti.
Me osobne to pak casto odrazuje vubec nejakou zmenu v PHP delat, protoze si nejsme jisty kde to pak vyelze na produkci. Ano testovani je dobre, ale treba testovat nejkou view vrstvu jen kvuli tomu, abych pochytil ze jsem nekde nenapsal spatne jmeno metody mi prijde jako plytvani lidskymi zdroji.
Nevim no, po roce prace s PHP nemuzu byt zadny expert na nej, to snad ani nejde, nicmene stale se na ten jazyk proste nemuzu naladit a tapu.
Souhlasim s tebou. Muzu jedine poradit, ze trochu pomuze dobre IDE (treba PHPStorm). Porad to neni idealni, souhlasim.
Ta neshoda je podle mě vyvolaná právě výrazem „sčítání řetězců“. Řetězce se pochopitelně nedají sčítat v původním významu tohoto slova (dají se spojovat, skládat, konkatenovat…). Teprve protože se používá stejný operátor (sčítací plus) pro tyto dvě různé operace, tak se naskýtá formulace „sčítat řetězce“ a z toho vyplývající logika, že když sčítám řetězce, výsledkem by měl být řetězec.
…ked sa pozriem na datum kedy bol tento prispevok zverejneny a na fakt ze NIKTO – auto ani v reakciach nespomenul JAVASCRIPT STRICT MODE… Moderne browsery ho podporuju uz par rokov a riesi napriklad problem s globalnymi premennymi a dalsie veci… Pred par dnami som sa musel ponorit znovu do Perlu a po JavaScripte je to hruza – hlavne rozlisovanie co je scalar, co je dictionary, co je pole…
Python je pekny ale tiez nie genialny, dospel som tiez ku hviezdickovym konstrukciam ktore vyzerali ako pointre v C++ a prehladne to bolo rovnako…
Idealny jazyk neexistuje ale Javascript ma z mojho pohladu k nemu najblizsie. :)