Czy indeksy ujemne w tablicach JavaScript powinny mieć wpływ na długość tablicy?

84

W javascript definiuję taką tablicę

var arr = [1,2,3];

też mogę

arr[-1] = 4;

Teraz, jeśli to zrobię

arr = undefined;

Tracę również odniesienie do wartości przy arr [-1] .

Więc logicznie wydaje mi się, że arr [-1] jest również częścią arr .

Ale kiedy śledzę (bez ustawiania arr na niezdefiniowane)

arr.length;

Zwraca 3 zamiast 4 ;

Chodzi mi więc o to, że jeśli tablice mogą być używane z indeksami ujemnymi, te indeksy ujemne również powinny być częścią ich długości **. Nie wiem, może się mylę lub może mi brakuje jakiegoś pojęcia o tablicach.

me_digvijay
źródło
Dlaczego miałbyś używać indeksu ujemnego? Nie widzę sensu, chyba że czegoś mi brakuje.
elclanrs
11
Możesz też pisać arr[1.5] = 1i to też nie wpływa na długość. Specyfikacja języka jasno określa, co wpływa na długość. Może ci się to nie podobać, ale musisz z tym żyć. Albo to, albo zaprojektuj własny, konkurencyjny język i przekonaj ludzi, aby na niego przeszli.
Raymond Chen
1
@DigvijayYadav: Można to uznać za wadę lub funkcję, podobnie jak wiele innych rzeczy w JavaScript. Dzieje się tak, ponieważ tablice zachowują się trochę jak obiekty, możesz także użyć łańcucha, var a = []; a['foo'] = 'baz'ale to nie znaczy, że powinieneś; jest to wyraźnie sprzeczne ze wszystkimi konwencjami.
elclanrs
2
Chłopaki, głównym punktem tutaj jest to, że TABLICE SĄ PRZEDMIOTAMI. Nie ma różnicy. To jest powód tego zachowania i jest to całkowicie celowe, nawet jeśli ci się to nie podoba. Poczekaj, aż dowiesz się, że WSZYSTKIE liczby są przedstawiane jako zmiennoprzecinkowe, nawet jako liczby całkowite. JavaScript to ciekawy język ...
Tony R
2
Szczegóły algorytmu definiującego ustawienie elementów tablicy można znaleźć w specyfikacji: es5.github.com/#x15.4.5.1. ( szczególnie krok 4), a „indeks tablicy” jest zdefiniowany tutaj: es5.github.com /#x15.4 .
Felix Kling

Odpowiedzi:

109

Więc logicznie wydaje mi się, że arr [-1] jest również częścią arr.

Tak, ale nie w taki sposób, w jaki myślisz.

Możesz przypisać dowolne właściwości do tablicy (tak jak każdy inny obiekt w JavaScript), co robisz, gdy „indeksujesz” tablicę -1i przypisujesz jej wartość. Ponieważ nie jest to element tablicy, a jedynie dowolna właściwość, nie należy się spodziewać lengthrozważania tej właściwości.

Innymi słowy, poniższy kod robi to samo:

var arr = [1, 2, 3];

​arr.cookies = 4;

alert(arr.length) // 3;
Andrew Whitaker
źródło
31
Ok, oznacza to, że indeksy ujemne nie działają tak jak indeksy rzeczywiste.
me_digvijay
4
Racja, to po prostu dowolne właściwości w tablicy.
Andrew Whitaker
Ale co, jeśli chcę używać indeksów dodatnich jako właściwości, a nie indeksów, i nie chcę, aby miały one udział w długości tablicy?
me_digvijay
7
Więc nie używaj tablicy, użyj zwykłego starego obiektu. Ale w takim przypadku stracisz wbudowaną lengthwłasność.
Andrew Whitaker
2
@Digvijay: Konsola pokazuje tylko właściwości z dodatnimi nazwami całkowitymi, ponieważ (zakładam) po prostu wykonuje zwykłą forpętlę od 0do .length. Zrób, console.dir(arr)aby zobaczyć cały obiekt. Ponadto używanie notacji nawiasów do uzyskiwania dostępu do tablic nie jest charakterystyczne dla tablic, jest nieodłączną cechą obiektów ( var obj = {foo: 'bar'}; obj.foo or obj['foo']).
Felix Kling
25

lengthNieruchomość wróci numer o jeden wyższy od najwyższego przypisanego „Index”, gdzie Array „indeksy” są liczbami całkowitymi większa lub równa zeru. Zwróć uwagę, że JS zezwala na „rzadkie” tablice:

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

Oczywiście, jeśli nie ma elementów, to lengthjest 0. Zauważ również, że lengthnie zostanie zaktualizowany, jeśli użyjesz deletedo usunięcia najwyższego elementu.

Ale tablice są obiektami, więc możesz przypisywać właściwości z innymi dowolnymi nazwami właściwości, w tym liczbami ujemnymi lub ułamkami:

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

Zauważ, że za kulisami JS konwertuje nazwy właściwości na łańcuchy, nawet jeśli podasz liczbę, taką jak -1. (Indeksy dodatnich liczb całkowitych również stają się łańcuchami).

Metody tablicowe, takie jak .pop(), .slice()itp., Działają tylko na zerowych lub wyższych liczbach całkowitych „indeksach”, a nie na innych właściwościach, więc lengthsą spójne w tym punkcie.

nnnnnn
źródło
Dziękuję, twoja odpowiedź bardzo pomogła. Wypróbowałem również metodę łączenia z indeksem ujemnym, stwierdziłem, że jeśli użyję -1, to idzie do ostatniego elementu tablicy, dla -2 przechodzi do przedostatniego elementu i tak dalej.
me_digvijay
+1. Długość nie mówi, ile jest elementów ani ile ma właściwości. To po prostu najwyższy indeks + 1, np. Podany var a = new Array(9), ama długość 9 i nie ma żadnych elementów.
RobG
1
@DigvijayYadav - Tak, Array.slice()metoda ma to zrobić. String.slice()Metoda robi to samo.
nnnnnn
7

Zwróć uwagę, że gdy używasz indeksu pozycji (lub 0), wartości są umieszczane w tablicy:

var array = [];

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

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

Nie dzieje się tak, gdy dodajesz wartości bez indeksu (nie 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

Wartości są umieszczane w tablicy tylko wtedy, gdy grasz zgodnie z zasadami. Jeśli tego nie zrobisz, nie zostaną zaakceptowani. Są jednak akceptowane w samym obiekcie Array, co ma miejsce w przypadku prawie wszystkiego w JavaScript. Mimo że ["Foo", "Bar"]są to jedyne wartości w naszej tablicy, nadal możemy uzyskać dostęp "Fizzbuzz":

array[-1]; // "Fizzbuzz"

Ale zauważ jeszcze raz, że nie jest to część wartości tablicy, ponieważ jej „indeks” jest nieprawidłowy. Zamiast tego został dodany do tablicy jako kolejny element członkowski. Moglibyśmy uzyskać dostęp do innych elementów tablicy w ten sam sposób:

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

Zauważ tutaj, że uzyskujemy dostęp do popmetody w tablicy, która informuje nas, że zawiera kod natywny. Nie uzyskujemy dostępu do żadnej z wartości tablicy za pomocą klucza „pop”, ale raczej do elementu członkowskiego samego obiektu tablicy. Możemy to dodatkowo potwierdzić, jadąc na rowerze po publicznych członkach obiektu:

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

Który wypluwa:

 0 Foo
 1 Bar
-1 Fizzbuzz

Więc jeszcze raz, to na tym obiekcie , ale to nie jest w tej tablicy .

Świetne pytanie! Na pewno sprawiło, że zrobiłem podwójną próbę.

Sampson
źródło
1
+1 Nawet specyfikacja stwierdza, że ​​właściwości z dodatnią numeryczną nazwą właściwości nazywane są elementami tablicy (a każda inna właściwość nie jest): es5.github.com/#x15.4 .
Felix Kling
Wynik nie jest: ["Foo", "Bar"]ale ["Foo", "Bar", -1: "Fizzbuzz"]sprawdź tutaj skrzypce . Tak jak napisałeś, staje się właściwością z kluczem -1, ale jest to zdecydowanie widoczne na wyjściu. W przeciwnym razie twoja odpowiedź jest ładna i kompletna. Jeśli się zmienisz, ponownie zagłosuję za, mój głos był ostry, ale nie mogę go usunąć; czas minął.
Wilt
5

Jeśli naprawdę chcesz, aby indeksy ujemne i inne indeksy były uwzględnione w długości, utwórz tę funkcjonalność samodzielnie:

function getExtendedArray() {
    var a = [];

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

    return a;
}

Wtedy na przykład:

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
źródło
A jeśli chcesz, aby długość obejmowała tylko numerowane indeksy / właściwości, możesz dodać instrukcję if dla klucza - if(Number(key) != NaN) length++;Jeśli chcesz odfiltrować zmiennoprzecinkowe, dodaj && key % 1 == 0do warunku
Bonnev
4

Tablica to po prostu specjalny rodzaj obiektu w JS. Istnieje specjalna składnia, która jest dobra, ponieważ pozwala nam efektywnie z nimi pracować. Wyobraź sobie, jak żmudne byłoby pisanie literału tablicowego w ten sposób:

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

Niemniej jednak nadal są obiektami . Więc jeśli to zrobisz:

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

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

Te nie-całkowite właściwości zostaną dołączone do obiektu (którym jest tablica), ale nie są dostępne dla niektórych metod natywnych, takich jak Array.length.

Można założyć, że natywna metoda „length” jest metodą pobierającą, która zlicza wszystkie właściwości (a indeksy właściwościami, a wartości są wartościami rzeczywistymi ), które można zamienić na dodatnią liczbę całkowitą lub zero. Coś takiego jak ta pseudo-implementacja:

Ze względu na specyfikacjelength metoda natywna - jako metoda pobierająca ( exampleArray.length) - sprawdzi wszystkie nieujemne liczby całkowite mniejsze niż 2 32 klucze ”, największy z nich i zwróci jego wartość numeryczną + 1.

Jako setter ( exampleArray.length = 42), utworzy kilka pustych pozycji dla `` brakujących '' indeksów, jeśli dana długość jest większa niż rzeczywista liczba nieujemnych kluczy całkowitych, lub usunie wszystkie nieujemne klucze całkowite (i ich wartości), które nie są większe niż podana długość.

Pseudo-implementacja:

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

HynekS
źródło
3

Tablice w JavaScript są w rzeczywistości obiektami. Są po prostu prototypowane z konstruktora Array.

Indeksy tablicowe są w rzeczywistości kluczami w tablicy mieszającej, a wszystkie klucze są konwertowane na ciągi. Możesz utworzyć dowolny klucz (np. „-1”), ale metody w Array są dostosowane do działania jak tablica. Tak więc lengthnie jest to rozmiar obiektu, a jedynie gwarantuje, że będzie większy niż największy indeks całkowity. Podobnie, drukowanie arrwyświetli tylko wartości z kluczami całkowitymi> = 0.

Więcej informacji tutaj.

Tony R.
źródło
dokładnie. lub moglibyśmy zacząć się zastanawiać, dlaczego tablica ['foo'] nie jest prawidłowym indeksem - javascript pozwala na to, ale nie oznacza, że ​​definicja tego, co tablica (i jej elementy) zmienia się z jednego języka na inny. działa zgodnie z przeznaczeniem.
tw airball
W rzeczywistości „tablice” nie istnieją w JS. Jednak podstawą JS są „tablice asocjacyjne”, znane również jako „obiekty”. Nie było więc wielkim skokiem, aby stworzyć obiekt Array imitujący zwykłą tablicę.
Tony R
1
Istnieje jedna bardzo szczególna różnica między tablicami a zwykłymi obiektami: samoregulująca właściwość length.
RobG
1

Według MDN:

Wartość właściwości length jest liczbą całkowitą ze znakiem dodatnim i wartością mniejszą niż 2 do potęgi 32 (232)

W Javascript możesz ustawić właściwość dowolnego tworzonego obiektu.

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

W ten sam sposób, gdy ustawisz indeks ujemny, zapisuje go jako właściwość w tablicy, a nie jako część indeksu.

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

Dlatego długość nie odzwierciedla tego, ale pokazuje to pętla for..in.

ktilcu
źródło
-1

W przypadku korzystania z indeksów niepozytywnych lub nieliczbowych tablica zachowuje się jak tablica asocjacyjna zawierająca pary klucz-wartość.

Aby iterować po tablicy, której możesz użyć

for(var index in myArray) {
  document.write( index + " : " + myArray[index] + "<br />");
}
Ashan
źródło