views:

104

answers:

6

I was in the middle of coding a slideshow in javascript using jquery, when I ran into something that was doing what my several lines of code was doing in a couple lines.

Problem is, I don't comprehend how it works so I can modify it.

var imgs = [
        'image1.jpg',
        'image2.jpg',
        'image3.jpg'];
        var cnt = imgs.length;

    $(function() {
        setInterval(Slider, 4000);
    });

    function Slider() {
    $('#imageSlide').fadeOut("slow", function() {
       $(this).attr('src', imgs[(imgs.length++) % cnt]).fadeIn("slow");
    });
    }

This is the line that gets me:

[(imgs.length++) % cnt]

I'm reading that as

3+1 % 3 = 1

Now every time it executes, none of that code appears to be modifying any of the variables. cnt will always equal imgs.length (3), imgs.length++ doesn't actually modify it, it just adds one for that single execution, correct?

So matter how many times it executes, it will always be imgs[1] yet when I execute the code, it runs properly through all the array objects.

EDIT:

I simply added alert(imgs.length); and confirmed that ++ does actually change the variable, but it still doesn't make sense to me.

The first run, imgs.length = 4 after ++. 4 % 3 = 1 so it should run array object [1] not [0]?

Second run, 5 % 3 = 2

Third run, 6 % 3 = 0

etc etc.. but it shouldn't ever reset. However, if I put a alert(imgs.length % cnt); it only returns 0, 1, 2 than it resets.

Why?

A: 

I'm not 100% sure in javascript but every other language I know imgs.length++ is the same as saying imgs.length = imgs.length + 1 so that should be getting modified.

I'd consider it a "clever trick" that should be documented if used at all. It kinda breaks the semantics of what length means, so while logically correct to a computer it doesn't quite make sense in relation to the property name

So I guess it actually does change the length, but I still wouldn't recommend using it without documenting it. A more clear method would be to declare a new var i = 0 and replace imgs.length++ with i++, because although it doesn't probably matter in this case, if you're doing anything else with the array, the length is now probably not what you expect it to be.

Regarding the edit:
6 % 3 is 0 not 3.
Modulo (in programming, slightly different in purely math term) means divide and take the remainder.
6 / 3 = 2 with a remainder of 0.
7 / 3 = 2 with a remainder of 1.
8 / 3 = 2 with a remainder of 2.
9 / 3 = 3 with a remainder of 0,

Davy8
so that actually modifies it? I was thinking it was taking `imgs.length` and adding 1 to it, not actually changing `imgs.length` itself by adding 1.
Jared
As @Matchu points out, length is a writable property of an array in javascript, so yes it does change the length. If it wasn't writable (not sure if there's such a thing as readonly property in JS) it would probably give an error. x++ is just a shortcut for saying x = x + 1
Davy8
+2  A: 

I'm sure you're aware that imgs.length++, if it were applied to any other variable, would be adding one to it, modifying the original variable.

var x = 1;
alert(x++); // alerts 1
alert(x); // alerts 2

Sure enough, that's what's going on.

The script increments the length of the imgs array on every run, so it moves from 3 to 4 to 5 to 6, etc. I'm not an expert on Javascript internals, so I'm not sure if that has any effect on performance, but if you understand that length is, in fact, a writable property, it all makes sense.

Matchu
Good point in pointing out a subtle case that might not be obvious, in that the first alert would display 1 and not 2, because the increment happens after the value is returned
Davy8
+3  A: 

the return value of imgs.length++ is 3 therefore 3 % 3 = 0 but imgs.length will be 4 and the imgs array will contain 4 items.

try it in console:

var x = [ 1, 2, 3 ]
x.length++
=> 3
x.length++
=> 4
x.length++
=> 5
x
[1, 2, 3, undefined, undefined, undefined]

so every time imgs.length++ is called itt will append one item to the array. so not a nice code but short :)

EDIT: the why is easy to answer, in the start it contains cnt number of elements, so the code will step on every image and the starts from the beginning of it. so if the imgs array contains 5 items it will step through the 5 items and starts from the beginning.

the only problem with this code, that the array will be increased every time and the memory will be eaten by the browser.

KARASZI István
the easy fix for the code should be to create a counter in the beginning and increment that one. `var cnt = counter = imgs.length;`and then `imgs.length++` can be replaced with `counter++`
KARASZI István
so it's actually adding more objects to the array? I understand now. cnt caches the original imgs.length and never changes as imgs.length changes. makes sense now...
Jared
not a nice solution, I agree
KARASZI István
Google Chrome's console doesn't show the other undefineds at the end. It's not clear to me if the undefined's are actually being stored as array values, or if Firefox is just showing them to you for presentation purposes. Either way, I'm not a fan of the implementation.
Matchu
maybe you can try `JSON.stringify(x)`edit: no, you cannot it works differently in Safari and in Firefox, so sad!
KARASZI István
A: 

(imgs.length++) % cnt

will cause the images to loop through every image, over and over.

the percentage sign (%) in JavaScript means it uses Modulus (division remainder) operator.

Examples:

  • 0 % 3 = 0
  • 1 % 3 = 1
  • 2 % 3 = 2
  • 3 % 3 = 0
  • 4 % 3 = 1
  • 5 % 3 = 2 etc...
Fini
I think the OP understands that much, and is confused as to *why* imgs.length++ is even increasing at all.
Matchu
Actually he is headed the right direction.... I've edited my OP
Jared
A: 

Well, every 4:th second that the function Slider runs it expands the array, so you get:

3+1 % 3 = 1
4+1 % 3 = 2
5+1 % 3 = 0

and so on :)

Nisse
A: 

If you want your code to be more readable in the future, I would personally suggest you change your code to something like this:

var imgs = [
    'image1.jpg',
    'image2.jpg',
    'image3.jpg'];
var i = 0;

$(function() { // This is a shortcut for $(document).ready();
    setInterval(Slider, 4000);
});

function Slider() {
   $('#imageSlide').fadeOut("slow", function() {
      i = (i+1) % imgs.length;
      $(this).attr('src', imgs[i]).fadeIn("slow");
   });
}
bradlis7