Sollten negative Indizes in JavaScript-Arrays zur Arraylänge beitragen?

83

In Javascript definiere ich ein Array wie dieses

var arr = [1,2,3];

auch ich kann tun

arr[-1] = 4;

Wenn ich es jetzt tue

arr = undefined;

Ich verliere auch den Verweis auf den Wert bei arr [-1] .

Für mich scheint es logischerweise so, als ob arr [-1] auch ein Teil von arr ist .

Aber wenn ich folge (ohne arr auf undefined zu setzen)

arr.length;

Es gibt 3 nicht 4 ;

Mein Punkt ist also, wenn die Arrays mit negativen Indizes verwendet werden können, sollten diese negativen Indizes auch Teil ihrer Länge sein **. Ich weiß nicht, ob ich falsch liege oder ob mir ein Konzept über Arrays fehlt.

me_digvijay
quelle
Warum sollten Sie einen negativen Index verwenden? Ich verstehe den Punkt nur, wenn mir etwas fehlt.
Elclanrs
11
Sie können auch schreiben arr[1.5] = 1und das hat auch keinen Einfluss auf die Länge. Die Sprachspezifikation ist sehr klar darüber, was die Länge beeinflusst. Sie mögen es vielleicht nicht, aber Sie müssen damit leben. Entweder das oder Sie entwerfen Ihre eigene Konkurrenzsprache und überzeugen die Leute, darauf umzusteigen.
Raymond Chen
1
@DigvijayYadav: Kann als Fehler oder Feature wie viele andere Dinge in JavaScript angesehen werden. Dies liegt daran, dass sich Arrays ein bisschen wie Objekte verhalten. Sie können auch eine Zeichenfolge verwenden. Dies var a = []; a['foo'] = 'baz'bedeutet jedoch nicht, dass Sie dies tun sollten. es ist eindeutig gegen alle Konventionen.
Elclanrs
2
Leute, der Hauptpunkt hier ist, dass ARRAYS OBJEKTE SIND. Es gibt keinen Unterschied. Das ist der Grund für dieses Verhalten, und es ist völlig beabsichtigt, auch wenn Sie es nicht mögen. Warten Sie einfach, bis Sie erfahren, dass ALLE Zahlen als Gleitkomma dargestellt werden, auch ganze Zahlen. JavaScript ist eine merkwürdige Sprache ...
Tony R
2
Details zum Algorithmus, der die Einstellung von Array-Elementen definiert, finden Sie in der Spezifikation: es5.github.com/#x15.4.5.1 (insbesondere Schritt 4) und "Array-Index" wird hier definiert: es5.github.com /#x15.4 .
Felix Kling

Antworten:

105

Für mich scheint es logischerweise so, als ob arr [-1] auch ein Teil von arr ist.

Ja, aber nicht so, wie Sie denken.

Sie können einem Array beliebige Eigenschaften zuweisen (genau wie jedes andere Objekt in JavaScript). Dies tun Sie, wenn Sie das Array bei "indizieren" -1und einen Wert zuweisen. Da dies kein Mitglied des Arrays und nur eine beliebige Eigenschaft ist, sollten Sie nicht erwarten length, diese Eigenschaft zu berücksichtigen.

Mit anderen Worten, der folgende Code macht dasselbe:

var arr = [1, 2, 3];

arr.cookies = 4;

alert(arr.length) // 3;
Andrew Whitaker
quelle
30
Ok, es bedeutet, dass negative Indizes nicht wie echte Indizes wirken.
me_digvijay
4
Richtig, sie sind nur beliebige Eigenschaften des Arrays.
Andrew Whitaker
Aber was ist, wenn ich positive Indizes als Eigenschaften verwenden möchte, die nicht wie Indizes sind, und ich nicht möchte, dass sie zur Arraylänge beitragen?
me_digvijay
7
Verwenden Sie dann kein Array, sondern ein einfaches altes Objekt. In diesem Fall verlieren Sie jedoch die eingebaute lengthEigenschaft.
Andrew Whitaker
2
@Digvijay: Die Konsole zeigt nur die Eigenschaften mit positiven Ganzzahlnamen an, da (ich nehme an) nur eine normale forSchleife von 0bis ausgeführt wird .length. Tun console.dir(arr), um das gesamte Objekt zu sehen. Die Verwendung der Klammernotation für den Zugriff auf Arrays ist kein Merkmal von Arrays, sondern ein inhärentes Merkmal von Objekten ( var obj = {foo: 'bar'}; obj.foo or obj['foo']).
Felix Kling
24

Die lengthEigenschaft gibt eine Nummer eins zurück, die höher als der höchste zugewiesene "Index" ist, wobei Array- "Indizes" Ganzzahlen größer oder gleich Null sind. Beachten Sie, dass JS "spärliche" Arrays zulässt:

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

Wenn es natürlich keine Elemente dann lengthist 0. Beachten Sie auch, dass das lengthnicht aktualisiert wird, wenn Sie deletedas höchste Element entfernen.

Arrays sind jedoch Objekte, sodass Sie Eigenschaften mit anderen beliebigen Eigenschaftsnamen einschließlich negativer Zahlen oder Brüche zuweisen können:

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

Beachten Sie, dass JS hinter den Kulissen die Eigenschaftsnamen in Zeichenfolgen konvertiert, auch wenn Sie eine Zahl wie angeben -1. (Die positiven Ganzzahlindizes werden auch zu Zeichenfolgen.)

Array Methoden, wie .pop(), .slice()etc., nur die Arbeit an den Null-oder-höhere ganzen Zahl „Indizes“, nicht auf anderen Eigenschaften, so lengthist konsistent zu diesem Punkt.

nnnnnn
quelle
Vielen Dank, dass Ihre Antwort sehr geholfen hat. Außerdem habe ich die Spleißmethode mit negativem Index ausprobiert. Wenn ich -1 verwende, geht sie zum letzten Element des Arrays, -2 zum vorletzten Element und so weiter.
me_digvijay
+1. Die Länge sagt Ihnen nicht, wie viele Elemente es gibt oder wie viele Eigenschaften es gibt. Es ist nur der höchste Index + 1. zB angegeben var a = new Array(9), ahat eine Länge von 9 und überhaupt keine Elemente.
RobG
1
@ DigvijayYadav - Ja, die Array.slice()Methode soll das tun. Die String.slice()Methode macht das Gleiche.
nnnnnn
7

Beachten Sie, dass bei Verwendung eines Positionsindex (oder 0-Index) Werte innerhalb des Arrays platziert werden:

var array = [];

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

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

Dies ist nicht der Fall, wenn Sie Nicht-Index-Werte hinzufügen (nicht 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

Werte werden nur dann in das Array eingefügt, wenn Sie die Regeln einhalten. Wenn Sie dies nicht tun, werden sie nicht akzeptiert. Sie werden jedoch für das Array-Objekt selbst akzeptiert, was bei fast allem in JavaScript der Fall ist. Obwohl dies ["Foo", "Bar"]die einzigen Werte in unserem Array sind, können wir dennoch auf Folgendes zugreifen "Fizzbuzz":

array[-1]; // "Fizzbuzz"

Beachten Sie jedoch erneut, dass dies nicht Teil der Array-Werte ist, da der "Index" nicht gültig ist. Es wurde stattdessen als weiteres Mitglied zum Array hinzugefügt. Wir könnten auf die gleiche Weise auf andere Array-Mitglieder zugreifen:

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

Beachten Sie hier, dass wir auf die popMethode im Array zugreifen , die uns darüber informiert, dass diese systemeigenen Code enthält. Wir greifen nicht mit dem Schlüssel "pop" auf einen der Array-Werte zu, sondern auf ein Mitglied des Array-Objekts. Wir können dies weiter bestätigen, indem wir über die öffentlichen Mitglieder des Objekts fahren:

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

Was Folgendes ausspuckt:

 0 Foo
 1 Bar
-1 Fizzbuzz

Also noch einmal, es ist auf dem Objekt , aber es ist nicht in dem Array .

Tolle Frage! Hat mich dazu gebracht, sicher ein Double-Take zu machen.

Sampson
quelle
1
+1 Sogar die Spezifikation besagt, dass Eigenschaften mit einem positiven numerischen Eigenschaftsnamen als Elemente des Arrays bezeichnet werden (und jede andere Eigenschaft nicht): es5.github.com/#x15.4 .
Felix Kling
Ergebnis ist nicht: ["Foo", "Bar"]aber hier Geige["Foo", "Bar", -1: "Fizzbuzz"] prüfen . Wie Sie geschrieben haben, wird es eine Eigenschaft mit dem Schlüssel -1, aber es ist definitiv in der Ausgabe sichtbar. Ansonsten ist Ihre Antwort nett und vollständig. Wenn Sie sich ändern, werde ich erneut upvoten, mein downvote war zu hart, kann es aber nicht entfernen; Zeit abgelaufen.
Wilt
5

Wenn Sie wirklich möchten, dass negative Indizes und andere Indizes in die Länge aufgenommen werden, erstellen Sie die Funktionalität selbst:

function getExtendedArray() {
    var a = [];

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

    return a;
}

Dann zum Beispiel:

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
quelle
Und wenn Sie möchten, dass die Länge nur die nummerierten Indizes / Eigenschaften enthält, können Sie eine if-Anweisung für den Schlüssel hinzufügen. - if(Number(key) != NaN) length++;Wenn Sie die float- Anweisungen herausfiltern möchten, fügen Sie && key % 1 == 0sie der Bedingung hinzu
Bonnev
3

Arrays in JavaScript sind eigentlich Objekte. Sie werden einfach vom Array-Konstruktor als Prototyp erstellt.

Array-Indizes sind tatsächlich Schlüssel in einer Hashmap, und alle Schlüssel werden in Zeichenfolgen konvertiert. Sie können einen beliebigen Schlüssel erstellen (dh "-1"), aber die Methoden in Array sind so angepasst, dass sie sich wie ein Array verhalten. Es lengthist also nicht die Größe des Objekts, sondern nur garantiert größer als der größte ganzzahlige Index. In ähnlicher Weise werden beim Drucken arrnur Werte mit Ganzzahlschlüsseln> = 0 aufgelistet.

Mehr Infos hier.

Tony R.
quelle
genau. oder wir könnten uns fragen, warum Array ['foo'] kein gültiger Index ist - Javascript ermöglicht dies, bedeutet aber nicht, dass sich die Definition dessen, was ein Array (und seine Elemente) von einer Sprache in eine andere ändert, ändert. wie vorgesehen arbeiten.
Tw Airball
Tatsächlich existieren "Arrays" in JS nicht. Aber "assoziative Arrays", auch als "Objekte" bekannt, bilden den Kern von JS. Es war also kein großer Sprung, ein Array-Objekt zu erstellen, das ein reguläres Array imitiert.
Tony R
1
Es gibt einen ganz besonderen Unterschied zwischen Arrays und einfachen Objekten: die Eigenschaft der selbstanpassenden Länge.
RobG
3

Array ist nur eine spezielle Art von Objekt in JS. Es gibt eine spezielle Syntax, die gut ist, weil sie es uns ermöglicht, effektiv mit ihnen zu arbeiten. Stellen Sie sich vor, wie mühsam es wäre, ein Array-Literal so zu schreiben:

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

Es gibt aber trotzdem Objekte . Wenn Sie dies tun:

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

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

Diese nicht ganzzahligen Eigenschaften werden an ein Objekt angehängt (welches Array ist), aber einige native Methoden wie z Array.length.

Sie können davon ausgehen, dass die native 'Längen'-Methode ein Getter ist, der alle Eigenschaften zählt (und Indizes sind Eigenschaften, während Werte die tatsächlichen Werte sind ), die in eine positive Ganzzahl oder Null konvertierbar sind. So etwas wie diese Pseudo-Implementierung:

Aufgrund von Spezifikationen überprüft die native lenghtMethode - als getter ( exampleArray.length) - alle 'nichtnegativen Ganzzahlen kleiner als 2 32 ' Schlüssel, holt die große Eins und gibt ihren numerischen Wert + 1 zurück.

Als Setter ( exampleArray.length = 42) werden entweder einige leere Slots für 'fehlende' Indizes erstellt, wenn die angegebene Länge größer als die tatsächliche Anzahl nichtnegativer Ganzzahlschlüssel ist, oder alle nichtnegativen Ganzzahlschlüssel (und ihre Werte) gelöscht, die nicht größer als die sind gegebene Länge.

Die Pseudo-Implementierung:

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
quelle
1

Laut MDN:

Der Wert der Eigenschaft length ist eine Ganzzahl mit einem positiven Vorzeichen und einem Wert von weniger als 2 zur 32-Potenz (232).

In Javascript können Sie eine Eigenschaft für jedes von Ihnen erstellte Objekt festlegen.

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

Auf die gleiche Weise wird beim Festlegen eines negativen Index dieser als Eigenschaft im Array und nicht als Teil des Index gespeichert.

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

Aus diesem Grund spiegelt die Länge sie nicht wider, aber eine for..in-Schleife zeigt sie an.

ktilcu
quelle
-1

Wenn Sie nicht positive oder nicht numerische Indizes verwenden, verhält sich das Array wie ein assoziatives Array mit Schlüssel-Wert-Paaren.

Zum Durchlaufen des Arrays können Sie verwenden

for(var index in myArray) {
  document.write( index + " : " + myArray[index] + "<br />");
}
Ashan
quelle