JavaScript je ZLO 2.0

nenávistné poznámky vzteklého kodéra

Procházení (minových) polí - bacha na for..in

Nezasvěcený by si mohl myslet že pole je taková nudná hnědá věc - na jaře to zvořu a zaseju, na podzim sklidím, nic na tom není.
Ne tak v JavaScriptu. Tam je s poli veliká legrace, občas se můžu úplně potrhat smíchy. V tomhle připomínají spíš pole minová než orná, už proto že v JavaScriptovém poli se kolikrát objeví díra a když se do něj přimotá něco co tam nepatří tak je to malér.

Pusťte si tenhle kód:

var arr = [0, 1, 2, 3, 4];
Array.prototype.sum = function () {
    var result = 0, i;
    for (i in this) {
        result = result + this[i];
    }
    return result;
}
alert(arr.sum());

Výsledek bude možná pro někoho překvapivý, v každém případě ale naprosto zcestný.

Cyklus for..in je velice užitečná věc která umožní projít všechny vlastnosti a metody objektu v cyklu. No, všechny vlastně ne, jenom ty “vyvolené” - obvykle projde všechny které nastaví uživatel a také většinu těch které nastaví systém. Například u pole projde všechny očíslované prvky ale atribut length či metodu concat ignoruje. To umožňuje v cyklu for..in projít velmi rozumným způsobem všechny prvky pole.

Když však někdo rozšíří Array.prototype nebo dokonce jeho Object.prototype, cyklus for.. in začne najednou fungovat jinak. A jinak obvykle znamená že špatně.

Z toho plyne první poučení: nikdy nepoužívejte cyklus for..in na procházení pole Array !

Dobře tedy, přidržme se této poučky a upravme náš příklad podle ní:

var arr = [0, 1, 2, 3, 4];
Array.prototype.sum = function () {
    var result = 0, i, j;
    for (i = 0, l = this.length; i < l; i++) {
        result = result + this[i];
    }
    return result;
}
alert(arr.sum());

Tohle funguje, takže by si člověk mohl myslet že je to dobře. To by se ale šeredně spletl.

Představte si že máte přidat nějakou malou funkčnost do neznámého systému kde už běhají jakési záhadné JavaScripty. Nu což, pokud jsou dobře zapouzdřené, neměl by být problém je ignorovat a svojí funkčnost tam prostě přidat. Přidáte tam tedy naši oblíbenou funkci Array.prototype.sort a ejhle - ty cizí skripty co donedávna běhaly jako na drátkách najednou vyhazují desítky chyb a shazují vám browser, ačkoli jste na ně ani nesáhli.

Poté co skončíte dlouhý, nepublikovatelný a poměrně hlasitý proslov na adresu ostatních kodérů, jejich skriptů, browserů a nespravedlivého světa všeobecně, možná zjistíte že problém je jinde. Jako obvykle, mezi židlí a klávesnicí.

Tím že jsme rozšířili prototyp Array jsme zároveň změnili všechna pole v celém JavaScriptu. Pak se ale není co divit že to neběží, vždyť jsme vlastně změnili pravidla hry za běhu. Pokud budeme chtít být “ti hodní”, uděláme to aspoň takto:

var arr = [0, 1, 2, 3, 4];
var sum = function (arr) {
    var result = 0, i, j;
    for (i = 0, l = arr.length; i < l; i++) {
        result = result + arr[i];
    }
    return result;
};
alert(sum(arr));

Z toho plyne druhé poučení, mnohem důležitější než to první: Nikdy nerozšiřujte Object.prototype, Array.prototype ani další “vestavěné” prototypy. Pokud možno nerozšiřujte žádné prototypy které jste sami nevytvořili. Mějte ohled k životnímu prostředí.

Z toho jasně plyne že frameworky typu Protoype a jQuery mootools jsou ve své současné podobě čistým zlem a vedou kodéry JavaScriptu na scestí. Pozor na ně!


A teď něco úplně jiného: Troop of 100



14 Komentářů k článku “Procházení (minových) polí - bacha na for..in”

  1. MiSHAK:

    No no pole se má procházet for(var i=0;i


  2. honza:

    Taky jsem na to kdysi narazil :)

    http://www.abclinuxu.cz/blog/plathel/2005/4/22/84632


  3. richard:

    MiSHAK: ano, vy to víte, já to vím taky, otázka však je zda to věděl i ten člověk který před třemi lety programoval JS k systému na kterém možná budete příští měsíc dělat.
    Mám tu zkušenost že většina javascriptistů na nějaké “best practice” zvysoka kašle, obvykle tyhle věci ani neznají takže je lepší když programuju tak abych i jejich případné excesy snadno přežil.

    Ještě bych doplnil že úplně správně by se pole mělo procházet takto:
    var i;
    for (i = 0; …)
    protože konstrukce for (var i…) by se nemusela líbit JSLintu a mohla by být pro někoho matoucí, neboť Javascript si pojem “lokální proměnná” vysvětluje malinko jinak než většina ostatních jazyků.


  4. gebauer:

    offtopic, proste jsem nenasel vhodnejsi misto kam to napsat: na tomhle webu nejak nefunguji rss. Skoda, chtel jsem si je dat do ctecky


  5. richard:

    gebauer: díky za upozornění, ale je to divné protože mě funguje a i většina přístupů v logu je z RSS čteček, takže se zdá že jiným lidem to funguje také.
    Nemůže to být závada “na Vašem přijímači”, například dočasný výpadek spojení nebo tak něco?


  6. numero:

    Mno já s for (i = 0; …) už začínnal tak já jsem v pohodě :) , to jsem rád že se nemusím zatěžovat :)


  7. karf:

    “Z toho jasně plyne že frameworky typu Protoype a jQuery jsou ve své současné podobě čistým zlem a vedou kodéry JavaScriptu na scestí. Pozor na ně!”

    Nejsem si zcela jist, ale pokud vím, tak jQuery základní objekty nerozšiřuje. jQuery je dost odlišně navržená než Prototype, proto bych je moc neházel do jednoho pytle. Ale mohu se mýlit, proto bych se chtěl autora zeptat (protože vypadá, že JS rozumí a obě knihovny má prokouknuté), jak to tedy je a z čeho vychází.

    (tak jsem si zkusil jen tak prohledat zdroják jQuery na výraz “prototype” a vyskytuje se tam jen na prvním řádku ve výrazu “jQuery.fn = jQuery.prototype = { ” … )


  8. richard:

    karf: máte absolutní pravdu a já se budu muset omluvit jQuery že jsem ho neprávem osočil - spletl jsem si totiž jQuery s mootools. jQuery skutečně operuje ve vlastním namespace a jediný jeho vážnější problém je že koliduje právě s Prototypem.
    Kolize se sice dá odstranit ale v reálném provozu to zdaleka nemusí být tak jednoduché jak autoři popisují.
    Za omyl se omlouvám a hned ho opravím.


  9. karf:

    Děkuji (za jQuery :-)


  10. Ondra:

    Ahoj,

    můj názor asi bude trochu zaujatý, protože JavaScript je můj nejoblíbenější jazyk. Je pravda, že v článku není nic o tom, že JS je špatný, ale já tam cítim pyčo. Chyby, které popisujete, nevycházejí z návrhu JavaScriptu, který je takřka dokonalý ;-) , ale z jeho chybné implementace. Je to prostě příliš košatý jazyk na to, aby jej kde kdo prakticky implementoval přesně podle teoretického modelu.

    Já se zabýval JavaScriptem skrz naskrz, úpravu prototypů vestavěných objektů jsem vzdal někdy kolem roku 1999, neboť to shazovalo jak MSIE, tak Netscape, až Mozilla se s tím dokázala nějak vypořádat. Takže prosím buďte k mému miláškovi ohleduplní, a uvědomte si, že prohlížeč, ve kterém zrovna čtete tenhle web, je nejspíš ten, který je z valné části naprogramován právě v JavaScriptu, a určitě na něm nepracuje jediný autor. M’kay?


  11. richard:

    Javasript by byl pěkný jazyk, kdyby autorům (nebo spíše jejich managerům) svého času neruplo v bedně a nepokusili se z toho udělat Javu.
    Co se mě týče, k dokonalosti toho JS chybí hodně - a nejde o implementační chyby. Nelze mu to ale zazlívat, protože vznikal strašně dávno a teprve v poslední době se začínají objevovat jazyky jako je Ruby a Groovy, kde si programátor může vybrat zda použije typové či netypové proměnné, zda bude používat OOP, funkcionální programování nebo kombinaci obojího atd. Zkrátka přebírají to nejlepší z JS, Javy i dalších jazyků.
    Jsem velmi zvědavý na ECMA 4 a na to jestli se chytí, protože na papíře to vypadá velice zajímavě.


  12. Ondra:

    Udělat Javu? Co přesně máš namysli? Já teda historii JS docela znám (aspoň myslím), a zdá se mi, že jeho vývoj se ubírá víceméně “lineárně” a stabilně jedním směrem :-) Nebo se pletu? Kdyžtak prosím vysvětli… dík

    Ruby a Groovy neznám, na Ruby jsem koukal na tutoriál a nějak se mi to nechtělo zalíbit… autor vybral k seznámení jakési způsoby dotazování a ukládání proměnných pomocí otazníku a vykřičníku, no mě se takové perlovité jednoznakové nuance hodně příčí a osobně jsem se postupem času dopracoval k tomu, že preferuji raději ukecané jazyky jako je Java. Ono potom u projektů, které existují leta, to člověk hodně ocení, stejně jako komentáře v poměru aspoň 1:3.


  13. Kyo:

    [12] “perlovité jednoznakové nuance” - kdepak, to nemá nic společného s Perlem, to s největší pravděpodobností do Ruby přišlo ze Scheme.


  14. richard:

    [12] Jak psal Kyo - třeba otazník v Ruby znamená fakt otázku, když tedy napíšeš myObject?, znamená to “je myObject definovaný? Jestli ano, použij ho”.
    Ušetří to hodně nepřehledných if-ů, můžeš pak psát myObject?.myMember?.memberMethod?.resultProperty?
    V JS bys musel psát
    if (myObject && myObject.myMember && myObject.member.memberMethod …)
    což je většinou prakticky nečitelný.


Přidejte komentář

For spam detection purposes, please copy the number 4235 to the field below:




Weblog "JavaScript je ZLO 2.0" pohání všelijak překopaný WordPress,
XHTML je skoro validní, celkem respektuje Dogma W4 ale ne úplně