Stefan B.: JavaScript: key "find" in leerem Array existiert

Mahlzeit,

dies ist mein erster Thread hier seit ... ich weiß nicht, seit wievielen Jahrzehnten (ich war wohl so um die 2000er herum mal relativ aktiv hier; mein alter Account funktioniert aber nicht mehr).

Jetzt hat mich aber doch ein verflixtes JavaScript-Problem dazu gebracht, mich hier nochmal anzumelden. Google hat auch nicht weitergeholfen…

Folgendes:

Ich habe ein leeres Array, und bevor ich ein Element mit einem String als Key einfüge, überprüfe ich, ob es unter diesem Key schon existiert:

var array = [];

if ('bind' in array) ... // false
if ('find' in array) ... // true; WTF?

OK, schaumermal:

array['bind'] // Ausgabe: undefined
array['find'] // Ausgabe: function find() { [native code] }

Aha! Die find()-Funktion ist also schuld!

Komisch: Einfügen eines neuen Elements mit dem Key "find" geht aber, obwohl es schon "existiert":

array['bind'] = 'Bind';
array['find'] = 'Find';

array['bind'] // Ausgabe: Bind
array['find'] // Ausgabe: Find

So, und nun? Wie kann ich die Existenz eines selbst eingefügten Elements anhand des Keys überprüfen, ohne dass Funktionen der Array-Klasse gleichen Namens "dazwischenfunken"?

VG Stefan

  1. Hi,

    Ich habe ein leeres Array, und bevor ich ein Element mit einem String als Key einfüge, überprüfe ich, ob es unter diesem Key schon existiert:

    var array = [];
    
    if ('bind' in array) ... // false
    if ('find' in array) ... // true; WTF?
    

    hm. Ich hätte da eher includes('needle') genommen.
    Oder indexOf('needle') (wenn ich die Position brauch).

    cu,
    Andreas a/k/a MudGuard

    1. Leider muss ich auf den Key prüfen und nicht auf den Elementnamen, da die Elemente, die da eingefügt werden sollen, nicht wie in meinem vereinfachten Beispiel ein einfacher String ist, sondern ein Objekt in der Form { name: '', value: 0 }

      1. Hallo Stefan,

        Leider muss ich auf den Key prüfen und nicht auf den Elementnamen, da die Elemente, die da eingefügt werden sollen, nicht wie in meinem vereinfachten Beispiel ein einfacher String ist, sondern ein Objekt in der Form { name: '', value: 0 }

        wie du schon korrekt schreibst, ist die Notation { name: '', value: 0 } ein Objekt und kein Array in Javascript.

        Javascript-Arrays sind auch eher wie Arrays in Java oder C(++) zu verstehen und nicht wie in PHP.

        Viele Grüße
        Robert

        1. Hi Robert,

          Javascript-Arrays sind auch eher wie Arrays in Java oder C(++) zu verstehen und nicht wie in PHP.

          ... oder wie die Hashes in Perl (hach, was geht das da alles einfach) ...

          VG Stefan

          1. Hallo Stefan,

            ... oder wie die Hashes in Perl (hach, was geht das da alles einfach) ...

            Wenn man's kann, ist alles einfach 😉

            Wenn man mit solidem Halbwissen loslegt, bleiben Überraschungen nicht aus. Auch in Perl nicht. Allerdings kann ich da nicht mitreden. Zu Perl habe ich ein solides Nichtwissen 🤔...

            Rolf

            --
            sumpsi - posui - obstruxi
            1. @@Rolf B

              Zu Perl habe ich ein solides Nichtwissen 🤔...

              Wo ist denn der Perl-Spezi des Forums eigentlich abgeblieben? 🤔

              😷 LLAP

              --
              „Man darf die Klimakrise doch echt jetzt nicht überbewerten.
              Es ist gar nicht klar, ob 2022 wieder Extremregen und Hochwasser bringt oder ob es ein ganz gewöhnliches Dürrejahr wird.“

              — @HalleVerkehrt
            2. Hallo,

              Zu Perl habe ich ein solides Nichtwissen 🤔...

              dann lass uns zusammenlegen - dann haben wir zweimal nichts. 😀

              Live long and pros healthy,
               Martin

              --
              Fische, die bellen, beißen nicht.
  2. Tach!

    Ich habe ein leeres Array, und bevor ich ein Element mit einem String als Key einfüge, überprüfe ich, ob es unter diesem Key schon existiert:

    Arrays in Javascript haben immer nur numerische Keys.

    var array = [];
    
    if ('bind' in array) ... // false
    if ('find' in array) ... // true; WTF?
    

    Damit prüfst du, ob ein Objekt bestimmte Eigenschaften hat. Ein Array ist gleichzeitig auch ein Objekt. Du überprüfst mit in nicht die Keys eines Arrays.

    array['bind'] // Ausgabe: undefined
    array['find'] // Ausgabe: function find() { [native code] }
    

    bind ist keine Eigenschaft von Array, find hingegen schon.

    Komisch: Einfügen eines neuen Elements mit dem Key "find" geht aber, obwohl es schon "existiert":

    Damit weist du der Instanz des Array-Objekts eine neue Eigenschaft zu. Das find von vorher war nicht direkt der Objekt-Instanz zugeordnet, sondern es wurde über die Prototype-Chain gefunden.

    So, und nun? Wie kann ich die Existenz eines selbst eingefügten Elements anhand des Keys überprüfen, ohne dass Funktionen der Array-Klasse gleichen Namens "dazwischenfunken"?

    Da es keine assoziativen Arrays (Key-Value-Sammlungen) in Javascript gibt, solltest du anders an die Sache rangehen. Du kannst statt eines Array ein einfaches Objekt nehmen. Und wenn die Reihenfolge der Elemente eine Rolle spielt, gibt es auch noch Map.

    dedlfix.

    1. Hi dedlfix,

      danke; das war mir so bisher nicht klar gewesen. Deine Antwort hat mir weitergeholfen. In der Tat brauche ich an dieser Stelle nicht unbedingt ein ein Array; ein Object oder eine Map genügen auch - und es funktioniert!👍

      In der von Dir verlinkten Doku wird sogar beim Vergleich von Map und Object von der Verwendung eines Objects abgeraten, da auch hier "Accidental Keys" enthalten sein könnten:

      An Object has a prototype, so it contains default keys that could collide with your own keys if you're not careful.

      VG Stefan

      1. Tach!

        In der von Dir verlinkten Doku wird sogar beim Vergleich von Map und Object von der Verwendung eines Objects abgeraten, da auch hier "Accidental Keys" enthalten sein könnten:

        Ja, das ist in der Tat so, aber praktisch spielt das kaum eine Rolle. Wenn du deine Instanz mit einem Objektliteral erstellst, also so

        const o = {};
        o.foo = 'bar;
        

        oder so

        const o = {
          foo: 'bar
        };
        

        dann hast du über die Prototype-Chain nur ein paar sehr technisch benannte Eigenschaften (alles von der linken Spalte, das mit Objekt.prototype beginnt), die kaum in anderen Anwendungsfällen vorkommen. Dazu können noch die kommen, die von dir oder einer 3rd-Party-Library an Objekt.prototype angefügt worden sind. Diese kannst du nur treffen, wenn du sie direkt ansprichst. Mit Objekt.keys()/.values()/.entries() über dein Objekt iteriert, bekommst du sie nicht zu fassen. for...in-Loops solltest du aber meiden, denn diese laufen auch über enumerable Eigenschaften der Prototypen.

        dedlfix.

        1. Tachchen dedlfix,

          Ja, das ist in der Tat so, aber praktisch spielt das kaum eine Rolle. [...]

          Das Problem ist, dass ich nicht weiß, welche Keys ich zur Laufzeit in das Object einlesen werde; theoretisch könnte es also wie in dem (nicht konstruierten) Beispiel von 'find' möglich sein, auch eine dieser "technisch" benannten Properties zu treffen. So eine Map ist daher tatsächlich das einzig Sichere - auch wenn ich jetzt herausgefunden habe, dass man mit

          var array = [];
          if (!array.hasOwnProperty('key'))
              ...
          

          herausfinden kann, ob ein Key selbst hinzugefügt wurde.

          Nur wenn der Key des hinzuzufügenden Elements dann zufälligerweise 'length' heißt, gibt's das nächste Problem...

              array['length'] = { name: Name, value: 0 };
              array['length'].value++;
          

          Bei Object gibt's zwar keine Property namens 'length', aber z.B. 'constructor'. - Das ist alles viel zu fehleranfällig... dann lieber doch gleich Maps. ;-)

          VG Stefan

  3. Hallo Stefan,

    Links zu unserem Wiki:

    Eine Map bringt Extrafunktionalität mit, wie die Beibehaltung der Zugangsreihenfolge. Wenn Du die nicht brauchst, kannst Du auch ein prototypenloses Objekt erzeugen. Es bringt auch ein paar Nanosekunden Performance, was aber nur auffällt, wenn Du viele Millionen Zugriffe machen musst.

    const objectWithProto = { };
    console.log(Object.getPrototypeOf(objectWithProto) == Object.prototype);
    // true
    
    const protoFreeObject = Object.create(null);
    console.log(Object.getPrototypeOf(protoFreeObject ));
    // null
    

    Bei einem prototyplosen Objekt kannst Du die Schlüsel erhalten, wenn Du mit for..in darüber läufst. for..in bezieht die vom Prototyp geerbten Eigenschaften übrigens nicht ein.

    Bei einer Map geht das nicht, mit for..in erhältst Du die Eigenschaften der Map - und sie hat keine eigenen. Hier musst Du mit der .forEach Methode arbeiten oder die Iterator-Schnittstelle nutzen, also die for..of-Schleife.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Hi Rolf,

      Bei einem prototyplosen Objekt kannst Du die Schlüsel erhalten, wenn Du mit for..in darüber läufst. for..in bezieht die vom Prototyp geerbten Eigenschaften übrigens nicht ein.

      So habe ich es vorher mit dem Array gemacht.

      Bei einer Map geht das nicht, mit for..in erhältst Du die Eigenschaften der Map - und sie hat keine eigenen. Hier musst Du mit der .forEach Methode arbeiten oder die Iterator-Schnittstelle nutzen, also die for..of-Schleife.

      Genau; wobei, for...of ist da die einfachere Variante.

      Wieder was gelernt.

      Danke und VG

      Stefan