Wie und warum funktioniert 'a' ['toUpperCase'] () in JavaScript?

209

JavaScript überrascht mich immer wieder und dies ist eine weitere Instanz. Ich bin gerade auf einen Code gestoßen, den ich zuerst nicht verstanden habe. Also habe ich es getestet und bin zu folgendem Ergebnis gekommen:

alert('a'['toUpperCase']());  //alerts 'A'

Nun muss dies offensichtlich sein, wenn toUpperCase()es als Mitglied vom Typ string definiert ist, aber es ergab für mich anfangs keinen Sinn.

Wie auch immer,

  • funktioniert das, weil toUpperCaseein Mitglied von 'a' ist? Oder ist hinter den Kulissen noch etwas los?
  • Der Code, den ich gelesen habe, hat folgende Funktion:

    function callMethod(method) {
        return function (obj) {
            return obj[method](); //**how can I be sure method will always be a member of obj**
        }
    }
    
    var caps2 = map(['a', 'b', 'c'], callMethod('toUpperCase')); // ['A','B','C'] 
    // ignoring details of map() function which essentially calls methods on every 
    // element of the array and forms another array of result and returns it
    

    Es ist eine generische Funktion, ANY- Methoden für JEDES Objekt aufzurufen . Bedeutet dies jedoch, dass die angegebene Methode bereits ein implizites Mitglied des angegebenen Objekts ist?

Ich bin sicher, dass mir ein ernsthaftes Verständnis des Grundkonzepts der JavaScript-Funktionen fehlt. Bitte helfen Sie mir, dies zu verstehen.

Mahesha999
quelle
24
Es gibt zwei Möglichkeiten, auf Eigenschaften eines Objekts zuzugreifen: Punktnotation und Klammernotation. Etwas verwandt: stackoverflow.com/a/11922384/218196 . Sie kennen die Klammernotation bereits, da Sie sie beim Zugriff auf Array-Elemente immer verwenden : arr[5]. Wenn Zahlen gültige Bezeichnernamen haben, können Sie die Punktnotation verwenden : arr.5.
Felix Kling
2
Es ist das gleiche wie 5['toString']().
0x499602D2
1
Ebenfalls verwandt: stackoverflow.com/q/4968406/218196 .
Felix Kling
Verwandte Lektüre: 1) Vererbung und die Prototypenkette: developer.mozilla.org/en-US/docs/JavaScript/Guide/… 2) Das geheime Leben von JavaScript-Grundelementen: javascriptweblog.wordpress.com/2010/09/27/…
Theraot
21
Beim ersten Lesen dachte ich, der Titel wäre "Wie und warum funktioniert JavaScript?" Ah, gut.
Problematisch

Antworten:

293

Um es zu brechen.

  • .toUpperCase() ist eine Methode von String.prototype
  • 'a'ist ein primitiver Wert, wird jedoch in seine Objektdarstellung konvertiert
  • Wir haben zwei mögliche Notationen, um auf Objekteigenschaften / -methoden zuzugreifen: Punkt- und Klammer-Notation

So

'a'['toUpperCase'];

ist der Zugriff über die Klammernotation auf dem Grundstück toUpperCasevon String.prototype. Da diese Eigenschaft auf eine Methode verweist , können wir sie durch Anhängen aufrufen()

'a'['toUpperCase']();
jAndy
quelle
Dies wäre eine lustige Interviewfrage
FluffyBeing
78

foo.barund foo['bar']sind gleich, so dass der Code, den Sie gepostet haben, der gleiche ist wie

alert('a'.toUpperCase())

Bei der Verwendung foo[bar](beachten Sie das Fehlen von Anführungszeichen) verwenden Sie nicht den Literalnamen, barsondern den Wert, den die Variable barenthält. Wenn Sie also die foo[]Notation anstelle von verwenden, foo.können Sie einen dynamischen Eigenschaftsnamen verwenden.


Werfen wir einen Blick auf callMethod:

Zunächst wird eine Funktion zurückgegeben, die objals Argument dient. Wenn diese Funktion ausgeführt wird, wird methoddieses Objekt aufgerufen . Die gegebene Methode muss also entweder entweder für sich objselbst oder irgendwo in ihrer Prototypenkette existieren.

Wenn toUpperCasediese Methode von stammt String.prototype.toUpperCase, wäre es ziemlich dumm, für jede einzelne vorhandene Zeichenfolge eine separate Kopie der Methode zu haben.

DiebMaster
quelle
25

Sie können entweder mit .propertyNameNotation oder ["propertyName"]Notation auf die Mitglieder eines Objekts zugreifen . Das ist das Merkmal der JavaScript-Sprache. Um sicherzustellen, dass sich das Mitglied im Objekt befindet, überprüfen Sie einfach, ob es definiert ist:

function callMethod(method) {
    return function (obj) {
        if (typeof(obj[method]) == 'function') //in that case, check if it is a function
           return obj[method](); //and then invoke it
    }
}
Artyom Neustroev
quelle
17

Grundsätzlich behandelt Javascript alles als Objekt, oder vielmehr kann jedes Objekt als Wörterbuch / assoziatives Array angesehen werden. Und Funktionen / Methoden werden für das Objekt genauso definiert - als Eintrag in diesem assoziativen Array.

Im Wesentlichen referenzieren / rufen Sie also die Eigenschaft 'toUpperCase' des Objekts 'a' (in diesem Fall ein Zeichenfolgentyp) auf (beachten Sie ' () ').

Hier ist ein Code auf meinem Kopf:

function myObject(){
    this.msg = "hey there! ;-)";
    this.woop = function(){
        alert(this.msg); //do whatever with member data
    }
}

var obj = new myObject();
alert( obj.msg );
alert( obj['msg'] );
obj['woop']();
Nisk
quelle
10

anyObject['anyPropertyName']ist das gleiche wie anyObject.anyPropertyNamewenn anyPropertyNamees keine problematischen Charaktere gibt.

Siehe Arbeiten mit Objekten im MDN.

Die toUpperCaseMethode ist an den Typ String angehängt. Wenn Sie eine Funktion für einen primitiven Wert aufrufen 'a', wird sie hier automatisch zu einem Objekt heraufgestuft, hier zu einem String :

In Kontexten, in denen eine Methode für eine primitive Zeichenfolge aufgerufen werden soll oder eine Eigenschaftssuche erfolgt, wird das Zeichenfolgenprimitiv von JavaScript automatisch umbrochen und die Methode aufgerufen oder die Eigenschaftssuche durchgeführt.

Sie können sehen, dass die Funktion vorhanden ist, indem Sie sie protokollieren String.prototype.toUpperCase.

Denys Séguret
quelle
9

Also in Javascript objectssind objects. Das heißt, sie sind von dieser Art {}. Objekteigenschaften können mit folgenden Optionen festgelegt werden: a.greeting = 'hello';oder a['greeting'] = 'hello';. Beide Wege funktionieren.

Das Abrufen funktioniert genauso. a.greeting(ohne Anführungszeichen) ist 'hello', a['greeting']ist 'hello'. Ausnahme: Wenn die Eigenschaft eine Zahl ist, funktioniert nur die Klammermethode. Die Punktmethode funktioniert nicht.

So 'a'ist ein Objekt mit 'toUpperCase'Eigenschaft, die eigentlich eine Funktion ist. Sie können die Funktion abrufen und anschließend so 'a'.toUpperCase()oder so aufrufen: oder 'a'['toUpperCase']().

Aber imo wäre der bessere Weg, die Kartenfunktion zu schreiben, wie

var caps = ['a','b','c'].map( function(char) { return char.toUpperCase(); } )

Wer braucht die callMethodFunktion dann?

Gier Boakye
quelle
Schön erklärt :-)
Elisha Senoo
6

Jedes JavaScript-Objekt ist eine Hash-Tabelle, sodass Sie auf seine Mitglieder zugreifen können, indem Sie einen Schlüssel angeben. Wenn eine Variable beispielsweise eine Zeichenfolge ist, sollte sie die Funktion toUpperCase haben. Sie können es also mit aufrufen

var str = "a"
str['toUpperCase'](). // you get the method by its name as a key and invoke it.

Also, durch Inline-Str, könnten Sie unten haben

"a"["toUpperCase"]()
Shuping
quelle
6

toUpperCase ist eine Standard-Javascript-Methode: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/toUpperCase

Der Grund dafür 'a'['toUpperCase']()ist, dass die Funktion toUpperCase eine Eigenschaft des Zeichenfolgenobjekts ist 'a'. Sie können die Eigenschaften eines Objekts mit object[property]oder referenzieren object.property. Die Syntax 'a''toUpperCase' gibt an, dass Sie auf die Eigenschaft 'toUppercase' des String-Objekts 'a' verweisen und es dann aufrufen ().

SteveP
quelle
4

Bedeutet dies jedoch, dass die angegebene Methode bereits ein implizites Mitglied des angegebenen Objekts ist?

Jemand könnte ein Objekt übergeben, das

  1. hat keine Eigenschaft mit dem Namen toUpperCase; oder
  2. hat eine Eigenschaft namens toUpperCase, die keine Funktion ist

Im ersten Fall wird ein Fehler ausgegeben, da der Zugriff auf eine nicht vorhandene Eigenschaft zurückgegeben undefinedwird und wir nicht undefinedals Funktion aufrufen können.

Im zweiten Fall wird ein Fehler ausgegeben, da wir wiederum keine Nichtfunktion als Funktion aufrufen können.

Denken Sie daran, dass JavaScript eine sehr lose typisierte Sprache ist. Es erfolgt nur eine geringe oder keine Typprüfung, es sei denn und bis dies erforderlich ist. Der von Ihnen angezeigte Code funktioniert in bestimmten Fällen, da in diesen Fällen das übergebene Objekt eine Eigenschaft namens toUpperCase, dh eine Funktion, hat.

Die Tatsache, dass das objArgument nicht die richtigen Eigenschaften aufweist, stört JavaScript sozusagen überhaupt nicht. Es nimmt eine abwartende Haltung ein und gibt keinen Fehler aus, bis zur Laufzeit ein tatsächliches Problem auftritt.

LarsH
quelle
4

Fast alles in Javascript kann als Objekt behandelt werden. In Ihrem Fall fungiert das Alphabet selbst als Zeichenfolgenobjekt und toUpperCasekann als Methode aufgerufen werden. Die eckigen Klammern sind nur eine alternative Möglichkeit, auf Objekteigenschaften zuzugreifen, und da dies toUpperCaseeine Methode ist, wird die einfache Klammer ()neben dem ['toUpperCase']Formen benötigt ['toUpperCase']().

'a'['toUpperCase']() ist äquivalent zu 'a'.toUpperCase()

'a'['toUpperCase']() // returns A
'a'.toUpperCase() // returns A
Sanjay
quelle
3

Das Wichtigste dabei ist, dass, da Javascript eine dynamische Sprache ist, jedes Objekt im Wesentlichen nur eine verherrlichte Hash-Map ist ( mit wenigen Ausnahmen ). Auf alles in einem Javascript-Objekt kann auf zwei Arten zugegriffen werden - in Klammernotation und Punktnotation.

Ich werde schnell die beiden Notationen durchgehen und den ersten Teil Ihrer Frage beantworten, und dann komme ich zum zweiten Teil.

Klammernotation

Dieser Modus ähnelt eher dem Zugriff auf Hashmaps und Arrays in anderen Programmiersprachen. Mit dieser Syntax können Sie auf jede Komponente (Daten (einschließlich anderer Objekte) oder Funktion) zugreifen .

Genau das tun Sie in Ihrem Beispiel. Sie haben 'a'eine Zeichenfolge (und kein Zeichenliteral, wie es in einer Sprache wie C ++ der Fall wäre).

Mit der Klammernotation greifen Sie auf die toUpperCaseMethode zu. Der Zugriff darauf reicht jedoch immer noch nicht aus. Durch einfaches Eingeben alertvon beispielsweise Javascript wird die Methode nicht aufgerufen. Es ist nur eine einfache Aussage. Um die Funktion aufzurufen, müssen Sie die Klammer hinzufügen: alert()Zeigt ein einfaches Dialogfeld an undefined, das keine Parameter enthält. Wir können dieses Wissen nun nutzen, um Ihren Code zu entschlüsseln.

alert('a'.toUpperCase());

Welches ist viel besser lesbar.

Ein guter Weg, dies ein bisschen besser zu verstehen, ist die Ausführung des folgenden Javascript:

alert(alert)

Dies ruft alertauf, indem es auch ein Funktionsobjekt übergibt alert, ohne auch die zweite Warnung auszuführen. Was angezeigt wird (zumindest in Chrome 26), ist Folgendes:

function alert() { [native code] } 

Berufung:

alert(alert())

zeigt zwei aufeinanderfolgende Meldungsfelder mit undefined. Dies ist leicht zu erklären: Das Innere alert()wird zuerst ausgeführt, zeigt undefined(weil es keine Parameter hatte) und gibt nichts zurück. Die äußere Warnung erhält den Rückgabewert der inneren Warnung - was nichts ist - und wird auch undefinedin einem Meldungsfeld angezeigt.

Probieren Sie alle Fälle auf jsFiddle aus!

Punktnotation

Dies ist der Standardansatz, mit dem auf Mitglieder eines Objekts mit dem .Operator dot ( ) zugegriffen werden kann . So würde Ihr Code in Punktnotation aussehen:

alert('a'.toUpperCase())

Viel besser lesbar. Wann sollten wir also die Punktnotation verwenden und wann sollten wir die Klammernotation verwenden?

Vergleich

Der Hauptunterschied zwischen den beiden Methoden ist die Semantik. Es gibt auch einige andere Details, aber ich werde gleich darauf zurückkommen. Am wichtigsten ist, was Sie tatsächlich tun möchten. Als Faustregel gilt, dass Sie die Punktnotation für gut etablierte Felder und Methoden eines Objekts verwenden und die Klammernotation, wenn Sie Ihr Objekt tatsächlich als Hash-Map verwenden .

Ein gutes Beispiel dafür, warum diese Regel so wichtig ist, finden Sie in Ihrem Beispiel. Da der Code die Klammernotation an einer Stelle verwendet, an der die Punktnotation viel sinnvoller gewesen wäre, ist der Code schwerer zu lesen. Und das ist eine schlechte Sache, denn Code wird viel öfter gelesen als geschrieben .

In einigen Fällen müssen Sie die Klammernotation verwenden, auch wenn die Punktnotation sinnvoller war:

  • Wenn ein Mitglied eines Objekts einen Namen hat, der ein oder mehrere Leerzeichen oder andere Sonderzeichen enthält, können Sie die Punktnotation nicht verwenden: foo.some method()funktioniert nicht, funktioniert aber foo["some method"]();

  • Wenn Sie dynamisch auf die Mitglieder eines Objekts zugreifen müssen, stecken Sie auch in der Klammernotation fest.

Beispiel:

for(var i = 0; i < 10; ++i) {
   foo["method" + i](); 
 }

Unter dem Strich sollten Sie die Klammer-Syntax verwenden, wenn Sie das Objekt als Hash-Map verwenden ( foods["burger"].eat()), und die Punkt-Syntax, wenn Sie mit "tatsächlichen" Feldern und Methoden arbeiten ( enemy.kill()). Da Javascript eine dynamische Sprache ist, kann die Grenze zwischen "tatsächlichen" Feldern und Methoden eines Objekts und "anderen" darin gespeicherten Daten ziemlich verschwommen werden. Aber solange Sie sie nicht auf verwirrende Weise mischen, sollte es Ihnen gut gehen.


Nun zum Rest Ihrer Frage (endlich !: P).

Wie kann ich sicher sein, dass method immer Mitglied von obj sein wird?

Das kannst du nicht. Versuch es. Versuchen Sie, derpeine Zeichenfolge aufzurufen . Sie erhalten eine Fehlermeldung in den Zeilen:

Uncaught TypeError: Object a has no method 'derp' 

Es ist eine generische Funktion, ANY-Methoden für JEDES Objekt aufzurufen. Bedeutet dies jedoch, dass die angegebene Methode bereits ein implizites Mitglied des angegebenen Objekts ist?

Ja, in deinem Fall müsste es sein. Andernfalls erhalten Sie den oben genannten Fehler. Allerdings müssen Sie nicht haben zu verwenden , return obj[method]();in der callMethod()Funktion. Sie können Ihre eigene Funktionalität hinzufügen, die dann von der Kartenfunktion verwendet wird. Hier ist eine fest codierte Methode, mit der alle Buchstaben in Großbuchstaben umgewandelt werden:

function makeCap()
{
    return function(obj) {
        return obj.toUpperCase();
    }
}

var caps2 = map(['a', 'b', 'c'], makeCap()); // ['A','B','C'] 
console.log(caps2)

Der Code in dem Tutorial, mit dem Sie verknüpft haben, verwendet Teilfunktionen . Sie sind ein kniffliges Konzept für sich. Mehr über dieses Thema zu lesen sollte helfen, die Dinge klarer zu machen, als ich sie jemals machen könnte.


Hinweis: Dies ist der Code der Kartenfunktion, die vom Code in der Frage verwendet wird. Quelle hier .

function map(arr, iterator) {
  var narr = [];
  for (var i = 0; i < arr.length; i++) narr.push(iterator(arr[i], i));
  return narr;
}
Andrei Bârsan
quelle
2

Wenn Sie fragen, wie es tatsächlich funktioniert, wie ich es gelesen habe. Ok, das ist eine einfache mathematische Funktion. Um es zu verstehen, müssen Sie sich die ASCII-Tabelle ansehen. Womit jedem Buchstaben ein numerischer Wert zugewiesen wird. Um dies zu verbergen, verwendet der Wettbewerb einfach eine logische Anweisung, um dies zu verbergen. Beispiel: Wenn (ChcrValue> 80 && charValue <106) // Dies ist die Menge der Kleinbuchstaben, dann charValue = charValue - 38; // der Abstand zwischen dem unteren und dem oberen Satz

So einfach ist das. Ich habe mich nicht wirklich darum gekümmert, die richtigen Werte nachzuschlagen. Dies verschiebt jedoch im Grunde alle Kleinbuchstaben in Großbuchstaben.

Daniel Haro
quelle
Wie hängt das mit der Frage zusammen?
Quasdunk
2
@glh, er fragte, wie und warum 'a'['toUpperCase']()arbeiten. Aber das Missverständnis ist verständlich, wenn jemand die Frage nicht gelesen hat.
LarsH
gr8 Antwort, da keine Stile text-transform: uppercasehinzugefügt wurden, war ich am Ende, wie JS es geschafft hat, den Fall zu ändern, Zweifel auszuräumen und ein oder zwei Dinge zu lernen. Vielen Dank.
dkjain