ugrás a tartalomhoz

A JavaScript, ami tömörödik

Joó Ádám · 2010. Júl. 28. (Sze), 21.26

A List Aparton megjelent Better JavaScript Minification című cikke folytatásában Nicholas C. Zakas újabb módszereket mutat be JavaScript kódunk tömöríthetőségének javítására.

Első írásában az eval() függvény és a with használatának kerülését hangsúlyozta.

A második részben a globális változók, az objektumtulajdonságok és a kulcsszavak érinthetetlenségéből fakadó tetemes elvesztegetett tömörítési potenciál megmentésére vonatkozóan ad hasznos tanácsokat.

A globális változók nem cserélhetők rövidebb nevekre, ugyanis a tömörítő nem tudhatja, azok mely más helyeken kerülnek esetleg felhasználásra. A cél tehát (mely egyébként is követendő szabály), hogy a lehető legkevesebbre csökkentsük a létrehozott globális változók számát, a készen kapottakat pedig azonnal tegyük lokálissá.

function doSomething() {
    var doc = document;

    // Itt a változó már lokális, tehát a neve rövidíthető
}

Az esetek nagy többségében ha a programunk több részéből is elérhető változóra van szükségünk, akkor sincs igény arra, hogy ezt más programok is elérjék: nem globális, csak a legfelsőbb szintű hatókörben elhelyezett változó kell nekünk. Egy önhívó névtelen függvény tehát ideális a célra:

(function () {
    var firstLongVariable, secondLongVariable;

    function doSomething() {
        // Ő eléri a két változót
    }

    function doOtherThing() {
        // És ő is
    }
})()

// Itt viszont már nem érhetők el

Az objektumtulajdonságok esetében hasonló a helyzet, mint a globális változóknál, azok nem rövidíthetők le. A megoldás is ugyanaz: az egynél többször használt kulcsot rendeljük egy helyi változóhoz, ami már biztonságosan átnevezhető a tömörítéskor. Mindig tartsuk észben, hogy ami pont után áll, az nem rövidíthető, így a célunk, hogy a pontok számát a lehető legkisebbre szorítsuk a kódunkban.

Ami a nyelv kulcsszavait illeti, ezek értelemszerűen nem cserélhetők le, így az egyetlen lehetőségünk, hogy eleve csökkentjük a számukat. A var esetén ezt megtehetjük az összevont deklaráció alkalmazásával.

var firstLongVariable;
var secondLongVariable;

helyett

var firstLongVariable, secondLongVariable;

A másik kulcsszó, melyen spórolhatunk, a return. Egyes iskolák egyébként is egyetlen visszatérési pontot ismernek csak el, mint üdvözítő gyakorlatot, tömörítési szempontból azonban vitán felül áll ezen tradíció követésének áldásos jellege.

switch (someValue) {
    case 'foo':
        return 1;
        break;
    case 'bar':
        return 2;
        break;
    case 'foobar':
        return 3;
        break;
    default:
        return 0;
}

sokkal hosszabb kimenetet eredményez, mint

var result = 0;

switch (someValue) {
    case 'foo':
        result = 1;
        break;
    case 'bar':
        result = 2;
        break;
    case 'foobar':
        result = 3;
        break;
}

return result;
A fenti technikák elsajátítása és alkalmazása akkor is jobb programozóvá tesz bennünket, ha nem kívánjuk tömöríteni a kódunkat; ha viszont igen, úgy egy kis odafigyeléssel sávszélességet és tárkapacitást spórolhatunk meg, elhanyagolható ráfordítással. 
1

switches példa

thgab · 2010. Júl. 29. (Cs), 06.13
A 2. switch-es példa hibás. A
var result = 0;
a switch elé kell, hogy kerüljön.
Másrészt, nem értem miért tömöríthető jobban a második switch példa,
hiszen az elsőből a break;-ek, a return miatt elhagyhatóak.
2

Másrészt, nem értem miért

Ajnasz · 2010. Júl. 29. (Cs), 09.24
Másrészt, nem értem miért tömöríthető jobban a második switch példa,

mert a return kulcsszo, igy nem cserelheto le 1 karakteres valtozonevre.
Ez a modszer nem csak switcheknel alkalmazhato, hanem barmely olyan fuggvenyben, ahol van visszateresi ertek. A fuggveny elejen definialsz egy valtozot amit a legvegen returnolsz, a fuggveny belsejeben pedig ennek adod a visszaadni kivant erteket.
3

a break sem, amit viszont a

Tyrael · 2010. Júl. 29. (Cs), 10.06
a break sem, amit viszont a minden agban return-os megoldasnal nem kellene kiirni.
legalabbis ezt probalja megertetni a kerdezo, ugy tunik sikertelenul.

a='valtozoHosszusaguErtek';break;
az imho hoszabb, mint a
return 'valtozoHosszusaguErtek';

legalabbis ha az ertek hoszabb mint 2-3 karakter.

es ugye a masodik esetben meg kellene egy
var a;
az elejere, meg egy
return a;
a vegere.

szoval ertem a lenyeget, csak rossz a pelda.

Tyrael
4

Igen, a pelda a rossz, ezert

Ajnasz · 2010. Júl. 29. (Cs), 13.35
Igen, a pelda a rossz, ezert probaltam altalanosan kifejeteni. Egy if/else jobb pelda lehetne:
if(a==b) {
  return 'asdf';
} else {
  return 'foobar';
}
vs
var o='foobar';
if(a==b) {
  o='asdf';
}
return o;
Plusz szerintem szebb is :]
6

Switch tipikusan az, amit nem

inf · 2010. Júl. 29. (Cs), 22.14
Switch tipikusan az, amit nem használ az ember, akkor már inkább külön metódusokba teszem a switch ágait, meg if-else-zek, vagy shortif-elek, ha már tömörítésről van szó.

return someValue=='foo'?1:someValue=='bar'?2:someValue=='foobar'?3:0;
Vagy map-be is szét lehet szedni a változatokat, ez az amivel a switchet legtöbbször kiváltom...

return (
{
	foo: 1,
	bar: 2,
	foobar: 3
})[someValue] || 0;
7

Gyakorlat

saxus · 2010. Júl. 29. (Cs), 22.58
Azért nekem picit több kódot tartalmazó switch-case-jaim szoktak lenni, mint egy szimpla értékválasztás, ott nem lehet megúszni ilyen egyszerűen.
9

De igen.

inf · 2010. Júl. 29. (Cs), 23.03
De igen.
10

return

Tyrael · 2010. Júl. 29. (Cs), 23.14
return someValue=='foo'?1:someValue=='bar'?2:someValue=='foobar'?3:0; 
ezert en eltornem a kezed, ha egy csapatban dolgoznank. :)
zarojelek nelkul (ami a forditonak felesleges, a fejlesztonek nem) hihetetlenul olvashatatlan, es nagyon konnyu benezni.

Vagy map-be is szét lehet szedni a változatokat, ez az amivel a switchet legtöbbször kiváltom...

persze, ahol hashmappel kivalthato, ott konnyu.
de mondjuk egy szokoev-szamito fuggvenyben, amiben az evszambol megmondod, hogy szokoev-e, ott mar nem tudsz hashmap-et csinalni a switch kimeneteleibol.

Tyrael
13

Js engedi, hogy zárójelek

inf · 2010. Júl. 30. (P), 12.04
Js engedi, hogy zárójelek nélkül menjen, pl php nem engedné, meg szerintem más nyelv sem. Szerintem nem könnyű benézni, ha valaki ért js-hez.
Teljesen analóg ezzel:

var r;
if (someValue=="foo"){r=1;}
else if (someValue=="bar"){r=2;}
else if (someValue=="foobar"){r=3;}
else {r=0;}
return r;
Amúgy meg mappel hosszabb kódrészek:

return (
	({
		foo: function (){return 1;},
		bar: function (){return 2;},  
		foobar: function (){return 3;}  
	})[someValue] || function (){return 0;}
)();
Más nyelvre is át lehet tenni, csak a feldolgozó metódusok nevei kellenek a map-be.

Ha kinyitsz bármilyen refaktorálásos könyvet, akkor kb első rész az, hogy a switch-ben lévő hosszabb részeket külön metódusokba kell tenni. Egyébként ha meg nem szeretnél több metódust, akkor sem jó a switch, mert elhagysz véletlenül egy break-et, aztán borul az egész, akkor már inkább if-else.
17

Szornyu

saxus · 2010. Júl. 31. (Szo), 01.19
Ne haragudjatok, de ezek egyre szornyubb megoldasok egy egesz egyszeru problemara...

Btw., aki egy if -nel az utasitasblokk kore nem rak zarojelet, annak mi kezet-labat torjuk.
18

Őszintén szólva annyira

inf · 2010. Júl. 31. (Szo), 05.13
Őszintén szólva annyira egyszerű a példa, hogy lusta voltam kitenni a zárójelet. De akkor közkívánatra:

return (
	(someValue=='foo')				//Ha a someValue változó értéke foo,
	?1								//akkor 1
	:(
		(someValue=='bar')			//különben, ha az érték bar,
		?2							//akkor 2
		:(
			(someValue=='foobar')	//különben, ha az érték foobar,
			?3						//akkor 3,
			:0						//az összes ellenkező esetben pedig 0
		)
	)
);									//a visszatérési érték.
Hát ez van, más pornót néz, nektek meg ez okoz örömöt... :D


Megírtam a map-es változatot is, hátha valakinek nem volt érthető. Ezek után már nem lehet tudni.

var MyClass=function (){};
MyClass.prototype.behaviourMap=
{
	foo: 'fooBehaviour',
	bar: 'barBehaviour',
	foobar: 'foobarBehaviour'
};
MyClass.prototype.getResult=function (someValue)
{
	var method=(someValue in this.behaviourMap)
		?this.behaviourMap[someValue]
		:this.defaultBehaviour;
	return this[method].call(this);
};
MyClass.prototype.fooBehaviour=function ()
{
	return 1;
};
MyClass.prototype.barBehaviour=function ()
{
	return 2;
};
MyClass.prototype.foobarBehaviour=function ()
{
	return 3;
};
MyClass.prototype.defaultBehaviour=function ()
{
	return 0;
};

var myInstance=new MyClass();
alert(myInstance.getResult("bar")); //2
20

tisztaban vagyok vele, hogy

Tyrael · 2010. Júl. 31. (Szo), 18.10
tisztaban vagyok vele, hogy mire jo a map, arrol beszelek, hogy nem lehet, es nem is kell minden switch-et map-re cserelni.

Tyrael
21

Szerintem sem, szituáció

inf · 2010. Júl. 31. (Szo), 21.24
Szerintem sem, szituáció függő, van ahol ez a jó megoldás, van, ahol más.
12

Köszönöm!

thgab · 2010. Júl. 30. (P), 10.50
Igen erre próbáltam rávilágítani. Lehet, hogy túl tömör voltam. :)
5

Jogos

Joó Ádám · 2010. Júl. 29. (Cs), 17.06
Köszönöm, javítottam. És valóban, egy feltételvizsgálat talán megfelelőbb lett volna az elágazás helyett.
8

Haszon?

saxus · 2010. Júl. 29. (Cs), 23.02
Az ilyen kódtömörítőknek mennyi szokott a hatékonysága lenni úgy általában?

És mennyi a sima kód gzippelve és a tömörítő kompatibilissé tett kód gzippelve? Csak mert egy ideje azon a véleményen vagyok, hogy ilyenre akkor maximum akkor van lehetőség, ha egy adott projektre "végtelen sok" idő áll rendelkezésre, gyakorlatban nem mindig van igény erre, maximum nagyon-nagyon-nagy oldalak esetén és általában inkább megéri az olvasható, könnyen karbantartható kódot szem előtt tartani.
11

nezd meg pl. a jquery

Tyrael · 2010. Júl. 29. (Cs), 23.24
nezd meg pl. a jquery project-en.
160k vs 70k
http://code.jquery.com/jquery-1.4.2.js
http://code.jquery.com/jquery-1.4.2.min.js

jobb helyeken a js/css minify-olas, osszebuildeles, gzippelest,, css spriteositas, etc. a deploy soran automatikusan megtortenek, szoval a fejlesztoknek csak akkor para a debugolas, ha konkretan ezen eljarasok valamelyike bugos (volt ilyenre is pelda. :P).

en azon az allasponton vagyok, hogy ezt egyszer kell csak jol megcsinalni, aztan automatizalhato, es nagyon megeri, hiszen az oldal renderelesenek nagy resze nem a html oldal letoltese, hanem a dom felepitese, statikus fajlok letoltese, css/jss ertelmezese teszi ki.

Tyrael
15

Automatizálás vs. odafigyelés

Joó Ádám · 2010. Júl. 30. (P), 13.02
Van ugye az automatizálható része, amit egyszer kell megcsinálni, de a fentiek épp arról szólnak, hogy milyen kódolási gyakorlattal lehet igazán hasznossá tenni az eszközt.
19

ezzel tisztaban vagyok, sehol

Tyrael · 2010. Júl. 31. (Szo), 18.08
ezzel tisztaban vagyok, sehol nem is vitattam, a minify-olas ertelmerol beszeltem (marmint hogy szerintem nagyon is van letjogosultsaga)
szoval nem igazan ertem, hogy itt most ezt miert irod le nekem.

Tyrael
22

Kiegészítés

Joó Ádám · 2010. Júl. 31. (Szo), 23.34
Csak az „egyszer kell megcsinálni”-hoz akartam hozzátenni.
24

ok, ezzel egyetertek. Tyrael

Tyrael · 2010. Aug. 3. (K), 22.42
ok, ezzel egyetertek.

Tyrael
14

Nézd meg a cikket

Joó Ádám · 2010. Júl. 30. (P), 13.01
Nézd meg a hivatkozott cikket, ott szép számértékek vannak.
16

Comment-ezés

Adam · 2010. Júl. 30. (P), 14.31
Ugyebár ha azt vesszük, a fenti módosításokon felül a kódtömörítők kiveszik az összes comment-et is a kódból, így nagyon hatékony tud lenni… feltéve, ha az ember normálisan comment-ezi a kódját. Ugye comment-eztek?
23

Closure Compiler

Ajnasz · 2010. Aug. 3. (K), 15.06
Erdemes ranezni a google closure compilerre is, ami YUI Compressorhoz kepest kb 5%-al jobb tomoritest eredmenyezhet es kodoptimalizaciot is vegez!
Viszont ezzel mar ovatosan kell banni ha ADVANCED_OPTIMIZATIONS opciot hasznaljuk, mert komoly valtoztatasokat vegez a kodon.