2

I wanted to make an associative array in Javascript in which I would have a bunch of string keys pointing to Image objects. The reason for this, is I wanted to go about pre-loading and dynamically controlling a lot of images without polluting the global space with hundreds of global variables. For example, to avoid the following:

var img1, img2, img3, ... , img99; 
img1 = img2 = img3 = ... = img99 = new Image();

Instead, I just wanted to call the image by some identifiable string name, to make the code easier to read, and to only have one _images variable in the global space.

At first, I attempted to use an Array for this task, only to find that there was no syntax for specifying instantiation of an Array with string keys. Further investigation led me here which went as far as to say that Array being used as an associative array could be harmful.

Previously, I had assumed that Javascript arrays were similar to LUA tables in which there was an indexed portion (whose size was kept in the length attribute) and a hash portion (string indexed). Apparently, that is not the case. Instead, when specifying a "string key" it is actually just shorthand for specifying an object attribute.

Thus, I was finally led down this path:

var _images = {
    "button_mouseover": new Image(),
    "button_mouseout": new Image(),
    "button_mousedown": new Image(),
    "button_mouseup": new Image()       
}

In which I create an object (whose reference is stored in _images) with a series of properties each storing a reference to an instantiated Image object.

Now to populate the src attribute of all the images, is to say the least, quite verbose:

_images["button_mouseover"].src = "fsfs.gif";
_images["button_mouseout"].src = "gsgs.gif";
_images["button_mousedown"].src = "agag.gif";
_images["button_mouseup"].src = "ggg.gif";

Finding the non-w3schools specs on the Image constructor, a default type, was hard in itself (there was a question on where it was!). Apparently, the constructor only takes an optional width and height, but no source attribute.

So, naturally, the next route I considered was the possibility of extending the Image constructor so that I could perhaps shorten this up a bit to:

var _images = {
    "button_mouseover": new Image("fsfs.gif"),
    "button_mouseout": new Image("gsgs.gif"),
    "button_mousedown": new Image("agag.gif"),
    "button_mouseup": new Image("ggg.gif")      
}

This led me to this question. The stigma there seemed to be, that it was possible, but not likely to work in all browsers and that extending default types is taboo.

That brings me to my questions then:

  1. Is extending the Image constructor really not going to work in some browsers?
  2. Is there some other Javascript object notation to do this that I've overlooked?

OR

Am I just stuck with having to list all the attributes, new Image() calls, and then a whole separate section with listing all the keys and sources again?

OR

Should I just use an auxiliary function, such as this:

function Image2( src ) {
    var t = new Image();
    t.src = src;
    return t;   
}

var _images = {
    "button_mouseover": Image2("fsfs.gif"),
    "button_mouseout": Image2("gsgs.gif"),
    "button_mousedown": Image2("agag.gif"),
    "button_mouseup": Image2("ggg.gif")     
}
Community
  • 1
  • 1
user17753
  • 3,083
  • 9
  • 35
  • 73
  • I guess my question is, why do you need to use a constructor function at all? What do you have in mind for the image objects after you've created them and assigned them to your hash? – kinakuta Mar 15 '12 at 20:22
  • I think you are searching for something called a loop. [`for..in`](https://developer.mozilla.org/en/JavaScript/Reference/Statements/for...in) perhaps? – Jon Mar 15 '12 at 20:23
  • `Further investigation led me here which went as far as to say that Array being used as an associative array could be harmful.` **Yes, absolutely.** The `Array` type is a special case of `Object` for numeric-keyed dense arrays. Use just the `Object` type for the "associative arrays" concept. – Lightness Races in Orbit Mar 15 '12 at 20:24
  • @Jon I can't really `for..in` against the `_images` object because I'm not really sure what all default properties are assigned to it (if any) and each image `src` is invariably unique so looping through each property for assignment would be sketchy at best for if I created a simple array of `src` values and assigned them in the `for..in` I'm not really sure if I'm guaranteed the [correct order](http://stackoverflow.com/questions/280713/elements-order-in-a-for-in-loop) etc but maybe. – user17753 Mar 15 '12 at 20:51

2 Answers2

7

The auxiliary function is probably your best bet, as it groups all the repeat code into a function.

Personally, I would do something like:

(function() {
    var img = function(src) {
        var t = new Image();
        t.src = src;
        return t;
    };
    window._images = {
        "over": img("fsfs.gif"),
        "out": img("gsgs.gif"),
        "down": img("agag.gif"),
        "up": img("ggg.gif")
    };
})();

Having shorter property names can help keep code reasonably-sized without losing too much readability, too.

Niet the Dark Absol
  • 320,036
  • 81
  • 464
  • 592
  • I hadn't considered grouping them together into a single, immediately invoked _anonymous_ function. I think that perhaps this was the capstone I was looking for regarding this issue. I know my question was vague at best, but I really like what you did with it here. – user17753 Mar 15 '12 at 20:36
  • It doesn't even need to be anonymous, or "immediately invoked". Just a normal function would do. – Lightness Races in Orbit Mar 15 '12 at 20:36
  • Right, but I think the advantage of this route is that I don't add an extra variable in the global space (e.g. the name I assign to the auxiliary function) that could inadvertently affect something else. – user17753 Mar 15 '12 at 20:44
1

The latter solution is the nicest one. Javascript objects are equivalent to associative arrays, or hash tables. Alternatively you can use a functional library like underscore to map the array:

var refs = {
  "button_mouseover": "fsfs.gif",
  "button_mouseout": "gsgs.gif",
  "button_mousedown": "agag.gif",
  "button_mouseup": "ggg.gif"     
}
var _images = _.map(refs, function(value) {
  var image = new Image();
  image.src = value;
  return image;
});
martyn
  • 131
  • 1
  • 3