Permalänk
Medlem

Optimering av javascript

Har en enkelt filterfunktion på en ul li lista där jag med hjälp av en selectbox filtrerar listan vid förändring.
Vill lära mig optimering av min kod och undrar om det finns något annat sett att skriva koden nedan?

$('select#filter').change(function() { var val = $(this).val(); if(val) { $('ul#data li').each(function () { if($(this).data('id') != val) { $(this).hide(); } else { $(this).show(); } }) } else { $('ul#data li').show(); } });

Visa signatur
Permalänk

@LightX01:

/* Deklarera utanför change-metoden så att de bara behöver letas upp i DOM'en en gång. Lägg till en klass på alla li-taggar som ska användas. $('ul#data li') <- Läser från höger till vänster. Så samtliga li-taggar i hela DOM'en väljs ut sedan kollas vilka som ligger i ul#data. */ var listItems = $('#data .list-items'); //Läs om event-bubbling $('document').on('change', '#filter', function() { var val = $(this).val(); if (val) { //En vanlig for-loop är snabbar än jQuery.each() for (var i = 0; i < listItems.length; i++) { $listItem = $(listItems[i]); $listItem.toggle($listItem.data('id') === val); } } else { listItems.show(); } });

Permalänk
Medlem
Skrivet av LightX01:

Har en enkelt filterfunktion på en ul li lista där jag med hjälp av en selectbox filtrerar listan vid förändring.
Vill lära mig optimering av min kod och undrar om det finns något annat sett att skriva koden nedan?

$('select#filter').change(function() { var val = $(this).val(); if(val) { $('#data li').each(function () { if($(this).data('id') != val) { $(this).hide(); } else { $(this).show(); } }) } else { $('ul#data li').show(); } });

Skrivet av Yxskaftet:

@LightX01:

/* Deklarera utanför change-metoden så att de bara behöver letas upp i DOM'en en gång. Lägg till en klass på alla li-taggar som ska användas. $('ul#data li') <- Läser från höger till vänster. Så samtliga li-taggar i hela DOM'en väljs ut sedan kollas vilka som ligger i ul#data. */ var listItems = $('#data .list-items'); //Läs om event-bubbling $('document').on('change', '#filter', function() { var val = $(this).val(); if (val) { //En vanlig for-loop är snabbar än jQuery.each() for (var i = 0; i < listItems.length; i++) { $listItem = $(listItems[i]); $listItem.toggle($listItem.data('id') === val); } } else { listItems.show(); } });

Beroende lite på vad du menar med optimering. Har du inte prestandaproblem så är lättläst kod den bästa optimeringen du kan göra. Javascript är ett funktionellt språk och du kan — och bör — undvika traditionella for-/while-loopar.

$('#filter').change(function () { var val = $(this).val(), items = $('ul#data li'); if (val) { items.hide(); items.find('[data-id="' + val + '"]').show(); // istället för items.find(...) kan du använda items.filter(function () { return $(this).data('id') === val }).show(); } else { items.show(); } })

Visa signatur

Kom-pa-TI-bilitet

Permalänk
Medlem

@Teknocide: Tack för ett bra svar på min fråga. Precis vad jag behövde.

Visa signatur
Permalänk
Medlem

Ytterst liten tweak på vad Yxskaft sa med extra kommentarer - Detta är snabbare än vad Teknocide sa.

En vanlig loop med cachad array length är mycket snabbare än jquery .find()

Hade det varit i min kodbas hade jag delat upp den i två funktioner också - En som binder, och en istället för den anonyma funktionen i .change() och sen hade jag skrivit precis som teknocide gjort - Det är snyggare, även om det är lite långsammare, men nu var ju optimering frågan

// Cacha sökningen utanför där vi binder den // Kom ihåg regeln höger till vänster - En sökning på ID behöver aldrig definiera typen, // så man skriver aldrig ul#idpåulen, man skriver alltid #idpåulen var listItems = $('#data .list-items'); //Tror inte du behöver inte binda till dokument, jquery delegerar själv $('#filter').on('change.filter', function() { // Vi namespacar alltid när vi binder så att vi kan köra .off() när vi vill (change.filter) // Cacha value så vi inte behöver slå upp det igen var val = $(this).val(); if (val) { //En vanlig for-loop är snabbar än jQuery.each() // Vi cachar också array length eftersem det är lite snabbare. //De flesta minimerare gör detta åt dig // En negativ loop är pyttelite snabbare än en vanlig var len = listItems.length; for (var i=len; i--;) { var $listItem = $(listItems[i]); $listItem.toggle($listItem.data('id') === val); } } else { listItems.show(); } });

Här är ett härligt svar på varför en negativ loop är snabbare: http://stackoverflow.com/questions/3520688/javascript-loop-pe...

Man kan kolla med jperf, men har så liten betydelse - Viktigt att cacha arrayens längd dock om man skriver for-loopar för hand.

Permalänk
Medlem

Det kan vara så att i moderna browsers nu för tiden är en helt vanlig for loop med cachad längd snabbast.

Permalänk
Medlem

Om du inte skriver produktionskod som ska köras 1 000 000 gånger om dagen och ni har problem med kapacitet så är optimisering inget du behöver tänka på. Nästan alla nybörjare oroar sig jättemycket och vill optimisera sin kod, ju fortare du lägger av den ovanan desto fortare kommer du kunna skriva kod. Tiden du slösar på att optimera kommer antagligen att vara mycket större än tiden du sparar. Dessutom brukar optimering orsaka mycket mer svårläslig kod vilket gör att all fortsatt kodning tar länge tid när du behöver läsa din optimerade kod igen.

Citat:

We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%

Vid laget du behöver bry dig om optimering så kommer du kunna tillräckligt om programmering för att veta hur du ska kunna söka upp information om det fallet där du behöver omtimera.

Permalänk
Hedersmedlem
Skrivet av Ernesto:

// Vi cachar också array length eftersem det är lite snabbare. //De flesta minimerare gör detta åt dig // En negativ loop är pyttelite snabbare än en vanlig var len = listItems.length; for (var i=len; i--;) { var $listItem = $(listItems[i]); $listItem.toggle($listItem.data('id') === val); }

Eftersom du använder en "negativ loop" så gör den extra raden med cachning av längduppslagning ingen nytta — den hade ändå bara hämtats en gång för att initiera din iterationsvariabel.

Liksom du noterade i inlägget efter så är det vanskligt att säga att en sak är snabbare än en annan utan tester, och i synnerhet vad gäller JavaScript, då det finns en uppsättning olika motorer att testa mot. Detta snabba test ger exempelvis några mätpunkter:

  • Den snabbaste loopen i en modern Chrome-instans (version 48.0.2541.0) är det översta testet, vilket också den mest "naiva" lösningen: en positiv loop utan explicit längdcachning. Den är någon procent snabbare än att cacha längduppslagningen i en positiv loop, och hela 40 % snabbare än en negativ loop som den du skrev ovan (det fjärde testfallet).

  • Även i Firefox 38 är översta testet snabbast, och då ca 45 % snabbare än den negativa loopen, som är långsammast.

  • I en antik Opera 12.16-instans så är dock översta varianten långsammast av de fyra, men den negativa for-loopen är fortfarande klart långsammare än en positiv loop med explicit längdcachning (den senare är ca 40 % snabbare än den negativa loopen).

Notera att detta inte nödvändigtvis reflekterar en modern IE-instans (och säkerligen inte en omodern IE-instans).

Hur kan det vara så? Det är upp till motortillverkarna att implementera och optimera sin tolkning av JavaScript, och exempelvis Chromes och Firefox motorer cachar vektorlängder internt ändå. Det är också generellt en bra tanke för motortillverkare att se till att det enklaste (och därmed troligen mest använda) tillvägagångssättet också är det snabbaste.

I detta fall ger det den lustiga sidoeffekten att de som försöker lägga tid på att mikrooptimera sina skript lägger krokben för sig själva om de inte hela tiden ser till att faktiskt testa sin lösning, och detta mot flera olika motorer. Dessutom, så även om man testar för dagen så kan det hända att morgondagens motorer optimerar något som gör att prestanda ändras igen. För "riktiga" produkter löser man detta genom att utöver rent funktionella testsviter även ha automatiserade prestandatester som körs mot en stor uppsättning motorer och omgivningar.

Visa signatur

Nu med kortare användarnamn, men fortfarande bedövande långa inlägg.

Permalänk
Medlem

Får tacka för alla intressanta svar jag fått i min tråd. Det uppskattas!
Har gett mig nya insikter inom området.

Visa signatur