93

In javascript I define an array like this

var arr = [1,2,3];

also I can do

arr[-1] = 4;

Now if I do

arr = undefined;

I also lose the reference to the value at arr[-1].

SO for me logically it seems like arr[-1] is also a part of arr.

But when I do following (without setting arr to undefined)

arr.length;

It returns 3 not 4;

So my point is if the arrays can be used with negative indexes, these negative indexes should also be a part of their length**. I don't know may be I am wrong or I may be missing some concept about arrays.

Sampson
  • 265,109
  • 74
  • 539
  • 565
me_digvijay
  • 5,374
  • 9
  • 46
  • 83
  • Why would you want to use a negative index? That's very confusing. I don't see the point unless I'm missing something. – elclanrs Nov 29 '12 at 03:58
  • 14
    You can also write `arr[1.5] = 1` and that also does not affect the length. The language specification is very clear about what affects the length. You may not like it but you have to live with it. Either that or design your own competing language and convince people to switch to it. – Raymond Chen Nov 29 '12 at 04:00
  • @elclanrs: This is not about just using negative indexes. If this facility is given by the language, then definitely there should be a proper reason for it.Otherwise this looks like a flaw. – me_digvijay Nov 29 '12 at 04:01
  • 2
    @DigvijayYadav: Could be considered a flaw or a feature like many other things in JavaScript. This is because arrays behave a bit like objects, you can also use a string `var a = []; a['foo'] = 'baz'` but that doesn't mean you should; it's clearly against all conventions. – elclanrs Nov 29 '12 at 04:03
  • 3
    Guys, the main point here is that ARRAYS ARE OBJECTS. There is no difference. That is the reason for this behavior, and it's completely intentional even if you don't like it. Just wait until you learn that ALL numbers are represented as floating point, even integers. JavaScript is a curious language... – Tony R Nov 29 '12 at 04:16
  • 2
    You can find details of the algorithm that defines the setting of array elements in the specification: http://es5.github.com/#x15.4.5.1.(especially step 4) and "array index" is defined here: http://es5.github.com/#x15.4. – Felix Kling Nov 29 '12 at 04:20
  • @Felix dear god, those links are rabbit holes =) – Tony R Nov 29 '12 at 04:23
  • @Tony: I know :D But if you really want to understand something, read the spec ;) – Felix Kling Nov 29 '12 at 04:26
  • possible duplicate of [Why array in JavaScript showing wrong length](http://stackoverflow.com/questions/13333000/why-array-in-javascript-showing-wrong-length) – Bergi Nov 29 '12 at 04:28
  • @FelixKling - I think given _that_ spec it might be more appropriate to say "If you really want to understand JavaScript have somebody else explain the spec to you"... – nnnnnn Nov 29 '12 at 05:06
  • I guess you wanted to do this because you wanted n analogy with python – Ali Saberi Mar 04 '16 at 14:08

8 Answers8

121

SO for me logically it seems like arr[-1] is also a part of arr.

Yes it is, but not in the way you think it is.

You can assign arbitrary properties to an array (just like any other Object in JavaScript), which is what you're doing when you "index" the array at -1 and assign a value. Since this is not a member of the array and just an arbitrary property, you should not expect length to consider that property.

In other words, the following code does the same thing:

​var arr = [1, 2, 3];

​arr.cookies = 4;

alert(arr.length) // 3;
maletor
  • 7,072
  • 7
  • 42
  • 63
Andrew Whitaker
  • 124,656
  • 32
  • 289
  • 307
  • 40
    Ok, It means negative indexes don't actually act like real indexes. – me_digvijay Nov 29 '12 at 04:10
  • 4
    Right, they're just arbitrary properties on the array. – Andrew Whitaker Nov 29 '12 at 04:11
  • But what if I want to use positive indexes as properties not like indexes and I don't want them to contribute to the array length? – me_digvijay Nov 29 '12 at 04:13
  • 7
    Then don't use an array, use a plain ol' object. But in that case you'll lose the built in `length` property. – Andrew Whitaker Nov 29 '12 at 04:13
  • Also when I define arr[-1] = 4, if I console the arr I get only the initial array. Not the whole object with -1 as a property. If I want to access the [-1] property I have to use arr[-1]. So if I have to do this, I don't see arrays as real objects. – me_digvijay Nov 29 '12 at 04:25
  • @Yadav See my answer. Then see Ashan's answer for how to print an object. – Tony R Nov 29 '12 at 04:26
  • 2
    @Digvijay: The console only shows the properties with positive integer names because (I assume) it just does an ordinary `for` loop from `0` to `.length`. Do `console.dir(arr)` to see the complete object. Also, using bracket notation to access arrays is not a characteristic of arrays, it's an inherent characteristic of objects (`var obj = {foo: 'bar'}; obj.foo or obj['foo']`). – Felix Kling Nov 29 '12 at 04:28
  • @DigvijayYadav: That's just the console representation which doesn't show you non-numeric properties on Array objects. It's still there. – Bergi Nov 29 '12 at 04:30
  • there's a great article on this up on medium which is worth a read; https://medium.freecodecamp.org/learn-these-javascript-fundamentals-and-become-a-better-developer-2a031a0dc9cf – Shawson Jul 24 '18 at 12:23
  • Is there a way to prevent code from adding arbitrary properties or negative indexes? Can "use strict" prevent this ? – VishwaKumar Apr 23 '19 at 15:57
26

The length property will return a number one higher than the highest assigned "index", where Array "indexes" are integers greater than or equal to zero. Note that JS allows "sparse" arrays:

var someArray = [];
someArray[10] = "whatever";
console.log(someArray.length); // "11"

Of course if there are no elements then length is 0. Note also that the length doesn't get updated if you use delete to remove the highest element.

But arrays are objects, so you can assign properties with other arbitrary property names including negative numbers or fractions:

someArray[-1] = "A property";
someArray[3.1415] = "Vaguely Pi";
someArray["test"] = "Whatever";

Note that behind the scenes JS converts the property names to strings even when you supply a number like -1. (The positive integer indexes also become strings, for that matter.)

Array methods, like .pop(), .slice(), etc., only work on the zero-or-higher integer "indexes", not on other properties, so length is consistent on that point.

nnnnnn
  • 147,572
  • 30
  • 200
  • 241
  • Thank you your answer helped a lot. Also I tried the splice method with negative index, I found that if I use -1 it goes to the last element of the array, for -2 goes to the second last element and so on. – me_digvijay Nov 29 '12 at 04:39
  • +1. Length doesn't tell you how many elements there are, or how many properties there are. It's just the highest index + 1. e.g. given `var a = new Array(9)`, `a` has a length of 9 and no elements at all. – RobG Nov 29 '12 at 04:42
  • 1
    @DigvijayYadav - Yes, the [`Array.slice()` method](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/slice) is supposed to do that. The [`String.slice()` method](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/slice) does the same thing. – nnnnnn Nov 29 '12 at 05:03
10

Note that when you use a position (or 0) index, values are placed within the array:

var array = [];

array[0] = "Foo";
array[1] = "Bar";

// Result: ["Foo", "Bar"]
// Length: 2

This is not the case when you add non-index values (not 0-9+):

var array = [];

array[0]  = "Foo";
array[1]  = "Bar";
array[-1] = "Fizzbuzz"; // Not a proper array index - kill it

// Result: ["Foo", "Bar"]
// Length: 2

Values are only placed in the array when you play by the rules. When you don't, they aren't accepted. They are however accepted on the Array object itself, which is the case with just about anything in JavaScript. Even though ["Foo", "Bar"] are the only values in our array, we can still access "Fizzbuzz":

array[-1]; // "Fizzbuzz"

But note again that this isn't part of the array values, since its "index" isn't valid. It was instead added onto the array as just another member. We could access other array members in the same fashion:

array["pop"]; // function pop() { [native code] }

Note here that we're accessing the pop method on the array, which informs us that this contains native code. We're not accessing any of the array values with a key of "pop", but rather a member on the array object itself. We can further confirm this by cycling over the public members of the object:

for (var prop in array) 
    console.log(prop, array[prop]);

Which spits out the following:

 0 Foo
 1 Bar
-1 Fizzbuzz

So again, it's on the object, but it's not in the array.

Awesome question! Caused me to do a double-take for sure.

Sampson
  • 265,109
  • 74
  • 539
  • 565
  • 1
    +1 Even the specification states that properties with a positive numerical property name are called *elements* of the array (and any other property is not): http://es5.github.com/#x15.4. – Felix Kling Nov 29 '12 at 04:24
  • Result is not: `["Foo", "Bar"]` but `["Foo", "Bar", -1: "Fizzbuzz"]` check [fiddle here](https://jsfiddle.net/wilt/pg8wrdLs/). Like you wrote it becomes a property with key -1 but it is definitely visible in the output. Otherwise your answer is nice and complete. If you change I will upvote again, my downvote was to harsh, but can't remove it; time expired. – Wilt Feb 05 '20 at 09:00
6

If you really want negative indexes and other indexes to be included in the length, then create the functionality yourself:

function getExtendedArray() {
    var a = [];

    Object.defineProperty(a, 'totalLength', { 
        get : function() { return Object.keys(a).length; }
    });

    return a;
}

Then for example:

var myArray = getExtendedArray();
console.log(myArray.totalLength); // 0
myArray.push("asdf");
myArray[-2] = "some string";
myArray[1.7777] = "other string";
console.log(myArray.totalLength); // 3
David Sherret
  • 101,669
  • 28
  • 188
  • 178
  • And if you want the length to include only the **numbered** indexes/properties you can add an if statement for the key - `if(Number(key) != NaN) length++;` If you want to filter out the float ones add `&& key % 1 == 0` to the condition – Bonnev Apr 17 '16 at 09:12
4

Arrays in JavaScript are actually objects. They are simply prototyped from the Array constructor.

Array indices are actually keys in a hashmap, and all keys are converted to strings. You can create any key (i.e. "-1"), but the methods on Array are tailored to act like an array. So length isn't the size of the object, it's rather only guaranteed to be larger than the largest integer index. Similarly, printing arr will only list values with integer keys >= 0.

More info here.

Tony R
  • 11,224
  • 23
  • 76
  • 101
  • exactly. or we could start wondering why array['foo'] isn't a valid index -- javascript allows you to do it, but doesnt mean that the definition of what an array (and its elements) change from one language to another. working as intended. – tw airball Nov 29 '12 at 04:29
  • In fact "arrays" don't exist in JS. But "associative arrays," also known as "objects," are at the core of JS. So it wasn't a huge leap to make an Array object that imitates a regular array. – Tony R Nov 29 '12 at 04:31
  • 1
    There is one very special difference between arrays and plain objects: the self-adjusting length property. – RobG Nov 29 '12 at 04:38
4

Array is just a special kind of Object in JS. There is a special syntax, which is good, because it allows us to work with them effectively. Imagine how tedious would it be to write an array literal that way:

const array = {0: 'foo', 1: 'bar', 2: 'baz'} //etc...

But they are still objects nevertheless. So if you do:

const myArray = [42, 'foo', 'bar', 'baz'];

myArray[-1] = 'I am not here';
myArray['whatever'] = 'Hello World';

Those non-integer properties will be attached to an object (which array is), but there are not accessible by some native methods like Array.length.

You can assume that the native 'length' method is a getter which counts all properties (and indexes are properties, while values are the actual values) convertible to positive integer or zero. Something like this pseudo-implementation:

Due to specs, the native length method – as a getter (exampleArray.length) – will check all 'nonnegative integer less than 232' keys, get the greatest one and return its numeric value + 1.

As a setter (exampleArray.length = 42), it will either create some empty slots for 'missing' indices if the given length is greater than the actual number of nonnegative integer keys, or delete all nonnegative integer keys (and their values) which are not greater then the given length.

The pseudo-implementation:

const myArray = {
  '-1': 'I am not here', // I have to use apostrophes to avoid syntax error
  0: 42,
  1: 'foo',
  2: 'bar',
  3: 'baz',
  whatever: 'Hello World',

  get myLength() {
    let highestIndex = -1;
    for (let key in this) {
      let toInt = parseInt(key, 10);
      if (!Number.isNaN(toInt) && toInt > -1) {
        // If you're not an integer, that's not your business! Back off!
        if (toInt > highestIndex) highestIndex = toInt;
      }
    }
    return highestIndex + 1;
  },
  set myLength(newLength) {
    /* This setter would either:
      1) create some empty slots for 'missing' indices if the given length is greater than the actual number of non-negative-integer keys
      2) delete all non-negative-integer keys (and their values) which are not greater then the given length.
    */
  }
}

console.log(myArray.myLength); // 4

myArray[9] = 'foobar';

console.log(myArray.myLength); // 10
Corman
  • 749
  • 11
  • 16
HynekS
  • 2,738
  • 1
  • 19
  • 34
1

According to MDN:

The value of the length property is an integer with a positive sign and a value less than 2 to the 32 power (232)

In Javascript you can set a property on any object you create.

var array = new Array();
array = [1,2,3];
array["boom"] = "pow";

In the same way when you set a negative index it stores it as a property on the array rather than part of the index.

array[-1] = "property does not add to array length";

This is why the length doesn't reflect it but a for..in loop shows it.

ktilcu
  • 3,032
  • 1
  • 17
  • 15
-1

When you use non-positive or non numeric indexes the array behaves as an associative array which has key-value pairs.

To iterate through the array you can use

for(var index in myArray) {
  document.write( index + " : " + myArray[index] + "<br />");
}
Ashan
  • 18,898
  • 4
  • 47
  • 67