简体   繁体   中英

Why does a new javascript array have 'undefined' entries?

Here's a sample that demonstrates an array that as you fill it in, it gets all types of undefined entries in it as well.

This is on firefox 19.0/firebug, not sure if it happens on other browsers.

Basic flow:

  1. Object is initialized (very bottom)
  2. It calls "load"
  3. When the ajax returns in load, data.objects contains an array of json objects. This array has no undefined entries on it.
  4. setObjects gets called where all the objects from the ajax call are copied to this.objects.
  5. As they are copied I can see undefined entries show up in firebug, If I didn't pass the array a second time and splice out the undefined entries it breaks mustache when trying to access the elements of the array in a template.

Why does javascript automatically pad out the this.objects array with undefined entries?

Here's the code:

function MailerFromProfile( )
{
    // privileged
    this.objects = [];
    this.load( );
}

MailerFromProfile.prototype.setObjects = function( objects )
{
    for( var i in objects )
    {
        if( 'undefined' !== objects[ i ] )
        {
            this.objects[ objects[ i ].id ] = objects[ i ];
        }
    }
    // I should not have to do this:
    for( var i = 0; i < this.objects.length; i++ )
    {
        if( typeof this.objects[ i ] === 'undefined' )
        {
            this.objects.splice( i, 1 );
            i--;
        }
    }
}

MailerFromProfile.prototype.setTemplate = function( n, v )
{
    this.template[ n ] = v;
}

MailerFromProfile.prototype.load = function( )
{
    jQuery.post(
        MAILER_PATH,
        { session: MAILER_SESSION,
          object : 'from_profile',
          action : 'list'
        },
        function( data )
        {
            if( typeof data.objects !== 'undefined' )
            {
                g_mailer_from_profiles.setObjects( data.objects );
            }
        },
        'json' );
}

var g_mailer_from_profiles = new MailerFromProfile( );

Why does javascript automatically pad out the this.objects array with undefined entries?

It doesn't, but it can look that way. :-)

The key bit is that you're setting array entries like this:

this.objects[ objects[ i ].id ] = objects[ i ];

...and so apparently, objects[ i ].id is sometimes higher than the number of entries in the array.

The standard JavaScript array isn't really an array at all (unless optimized by the JavaScript engine),¹ it's an object with special handling of a couple of things.

Let's take a simpler example:

var a = [];            // An empty array
console.log(a.length); // 0
a[3] = "foo";          // Puts an entry at index 3
console.log(a.length); // 4

As you can see, if you write to an array entry that's beyond the end of the array, the length of the array is adjusted to be one higher than the index you wrote to.

But JavaScript doesn't "pad out" the array with undefined ; the array is sparse . The entries at a[0] , a[1] , and a[2] don't exist :

console.log(1 in a);   // false

But if you ask an array for an entry that doesn't exist, you get undefined :

console.log(a[1]);     // undefined

...just like you do from any other JavaScript object when you ask for a property that doesn't exist (because array "elements" are actually object properties, and array "indexes" are actually property names).

That's different from an entry actually existing there with the value undefined , which is also possible:

a[1] = undefined;
console.log(1 in a);   // true
console.log(a[1]);     // undefined

So Mustache is looping through the array from 0 to length - 1 and asking for the entries, some of which don't exist, resulting in undefined .

Here's a runnable version of the various code snippets above:

 var a = []; // An empty array console.log(a.length); // 0 a[3] = "foo"; // Puts an entry at index 3 console.log(a.length); // 4 console.log(1 in a); // false console.log(a[1]); // undefined a[1] = undefined; console.log(1 in a);   // true console.log(a[1]);     // undefined

Which is a long way of saying, you may want to change that line to:

this.objects.push(objects[ i ]);

¹ That's a post on my anemic old blog.

When you do this

this.objects[ objects[ i ].id ] = objects[ i ];

you ask for the array to extend this.objects up to objects[ i ].id . There is no other solution for the engine than to give you undefined when you require the element at an unfilled index.

If your array is mostly empty (a sparse array), you should probably use an object as map instead, that is initialize it with

this.objects = {};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM