JavaScript continue de me surprendre et ceci est un autre exemple. Je viens de tomber sur un code que je n'avais pas compris au début. Alors je l'ai débogué et suis arrivé à cette conclusion:
alert('a'['toUpperCase']()); //alerts 'A'
Maintenant, cela doit être évident si toUpperCase()
est défini en tant que membre du type chaîne, mais cela n’a pas eu de sens pour moi au départ.
En tous cas,
toUpperCase
est un membre de 'a'? Ou il se passe autre chose dans les coulisses?le code je lisais a une fonction comme suit:
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
Il est un peu générique d’appeler [~ # ~] n’importe quelle [~ # ~] méthode sur [~ # ~] tout objet [~ # ~] . Mais cela signifie-t-il que la méthode spécifiée sera déjà un membre implicite de l'objet spécifié?
Je suis sûr qu'il me manque une compréhension sérieuse du concept de base des fonctions JavaScript. S'il vous plaît aidez-moi à comprendre cela.
Pour le décomposer.
.toUpperCase()
est une méthode de String.prototype
'a'
est une valeur primitive, mais est convertie en son représentation d'objetAlors
'a'['toUpperCase'];
est l'accès via notation entre crochets sur la propriété toUpperCase
, à partir de String.prototype
. Comme cette propriété fait référence à méthode, nous pouvons l'appeler en joignant ()
'a'['toUpperCase']();
foo.bar
Et foo['bar']
Sont égaux, le code que vous avez posté est le même que
alert('a'.toUpperCase())
Lorsque vous utilisez foo[bar]
(Remarque: absence de guillemets), vous n'utilisez pas le nom littéral bar
, mais quelle que soit la valeur que contient la variable bar
. Donc, utiliser la notation foo[]
Au lieu de foo.
Vous permet d'utiliser un nom de propriété dynamique.
Regardons callMethod
:
Tout d'abord, il retourne une fonction qui prend pour argument obj
. Lorsque cette fonction est exécutée, elle appelle method
sur cet objet. La méthode donnée doit donc exister soit sur obj
lui-même, soit quelque part sur sa chaîne de prototypes.
Dans le cas de toUpperCase
, cette méthode provient de String.prototype.toUpperCase
- il serait plutôt stupide de disposer d'une copie séparée de la méthode pour chaque chaîne existante.
Vous pouvez soit accéder aux membres de n'importe quel objet avec la notation .propertyName
Ou la notation ["propertyName"]
. C'est la caractéristique du langage JavaScript. Pour être sûr que le membre est dans l'objet, vérifiez simplement s'il est défini:
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
}
}
En gros, javascript considère tout comme un objet, ou plutôt chaque objet peut être vu comme un dictionnaire/tableau associatif. Et les fonctions/méthodes sont définies exactement de la même manière pour l'objet - en tant qu'entrée dans ce tableau associatif.
Donc, vous faites essentiellement référence/appelez (notez le ' () ') la propriété 'toUpperCase', du objet 'a' (qui est un type de chaîne, dans ce cas).
Voici un code du haut de ma tête:
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']();
anyObject['anyPropertyName']
est le même que anyObject.anyPropertyName
lorsque anyPropertyName
ne contient pas de caractères problématiques.
Voir Travailler avec des objets , à partir du MDN.
La méthode toUpperCase
est attachée au type String. Lorsque vous appelez une fonction sur une valeur primitive, voici 'a'
, il est automatiquement promu objet, ici un String :
Dans les contextes où une méthode doit être appelée sur une chaîne primitive ou une recherche de propriété est effectuée, JavaScript encapsule automatiquement la primitive de chaîne et appelle la méthode ou effectue la recherche de propriété.
Vous pouvez voir la fonction existe en enregistrant String.prototype.toUpperCase
.
Donc, en Javascript, objects
sont objects
. C'est qu'ils sont de cette nature {}
. Les propriétés d'objet peuvent être définies à l'aide de l'un de ces éléments: a.greeting = 'hello';
Ou a['greeting'] = 'hello';
. Les deux manières fonctionnent.
La récupération fonctionne de la même manière. a.greeting
(Sans les guillemets) est 'hello'
, a['greeting']
Est 'hello'
. Exception: si la propriété est un nombre, seule la méthode du crochet fonctionne. La méthode des points ne fonctionne pas.
Donc 'a'
Est un objet avec la propriété 'toUpperCase'
Qui est en fait une fonction. Vous pouvez récupérer la fonction et l'appeler par la suite de l'une des façons suivantes: 'a'.toUpperCase()
ou 'a'['toUpperCase']()
.
Mais je pense que la meilleure façon d’écrire la fonction map serait lavar caps = ['a','b','c'].map( function(char) { return char.toUpperCase(); } )
Qui a besoin de la fonction callMethod
alors?
Chaque objet JavaScript est une table de hachage. Vous pouvez donc accéder à ses membres en spécifiant une clé. Par exemple, si une variable est une chaîne, alors elle devrait avoir la fonction toUpperCase. Donc, vous pouvez l'invoquer par
var str = "a"
str['toUpperCase'](). // you get the method by its name as a key and invoke it.
donc, par inline str, vous pourriez avoir ci-dessous
"a"["toUpperCase"]()
toUpperCase est une méthode JavaScript standard: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/toUpperCase
La raison pour laquelle il fonctionne comme 'a'['toUpperCase']()
est que la fonction toUpperCase est une propriété de l'objet chaîne 'a'
. Vous pouvez référencer les propriétés d'un objet à l'aide de object[property]
ou object.property
. La syntaxe 'a' 'toUpperCase' indique que vous faites référence à la propriété 'toUppercase' de l'objet chaîne 'a', puis que vous l'appelez ().
Presque tout en javascript peut être traité comme un objet. Dans votre cas, l'alphabet lui-même agit comme un objet chaîne et toUpperCase
peut être invoqué comme méthode. Les crochets ne sont qu’un moyen alternatif d’accéder aux propriétés de l’objet et puisque toUpperCase
est une méthode, le simple crochet ()
Est nécessaire à côté de ['toUpperCase']
Pour former ['toUpperCase']()
.
'a'['toUpperCase']()
équivaut à 'a'.toUpperCase()
'a'['toUpperCase']() // returns A
'a'.toUpperCase() // returns A
Mais cela signifie-t-il que la méthode spécifiée sera déjà un membre implicite de l'objet spécifié?
Non, quelqu'un pourrait passer dans un objet qui
toUpperCase
; outoUpperCase
qui n'est pas une fonctionDans le premier cas, une erreur sera générée car l'accès à une propriété inexistante renvoie undefined
et nous ne pouvons pas invoquer undefined
en tant que fonction.
Dans le second cas, une erreur sera générée car, encore une fois, nous ne pouvons pas appeler une non-fonction en tant que fonction.
N'oubliez pas que JavaScript est un langage très typé. Peu ou pas de vérification de type ne se produit que si et jusqu'à ce que cela soit nécessaire. Le code que vous avez montré fonctionne dans certains cas parce que, dans ces cas, l'objet transmis a une propriété appelée toUpperCase
, qui est une fonction.
Le fait que l'argument obj
ne soit pas sûr d'avoir les bons types de propriétés ne dérange pas du tout JavaScript, pour ainsi dire. Il adopte une attitude attentiste et ne génère pas d'erreur tant qu'un problème réel ne survient pas au moment de l'exécution.
La chose importante à noter ici est que, puisque Javascript est un langage dynamique , chaque objet est essentiellement un hash-map glorifié ( avec quelques exceptions ). Et tout dans un objet Javascript est accessible de deux manières: la notation entre crochets et la notation par points.
Je passerai rapidement en revue les deux notations répondant à la première partie de votre question, puis je passerai à la deuxième partie.
Ce mode est plus similaire à l'accès à hashmaps et à des tableaux dans d'autres langages de programmation. Vous pouvez accéder à any composant (données (y compris d'autres objets) ou fonction) à l'aide de cette syntaxe.
C'est exactement ce que vous faites dans votre exemple. Vous avez 'a'
, Qui est une chaîne (et pas un caractère littéral, comme dans un langage tel que C++).
En utilisant la notation entre crochets, vous accédez à sa méthode toUpperCase
. Mais y accéder n'est toujours pas suffisant. taper simplement alert
, par exemple, en Javascript, n'appelle pas la méthode. C'est juste une simple déclaration. Pour appeler la fonction, vous devez ajouter la parenthèse: alert()
montre une boîte de dialogue simple contenant undefined
, car elle ne reçoit aucun paramètre. Nous pouvons maintenant utiliser cette connaissance pour déchiffrer votre code, qui devient:
alert('a'.toUpperCase());
Ce qui est beaucoup plus lisible.
En fait, un bon moyen de comprendre un peu mieux est d’exécuter le Javascript suivant:
alert(alert)
Ceci appelle alert
en lui transmettant un objet fonction, ainsi que alert
, sans exécuter également la deuxième alerte. Ce qui est affiché (dans Chrome 26, au moins) est le suivant:
function alert() { [native code] }
Appel:
alert(alert())
affiche deux boîtes de message consécutives contenant undefined
. C'est facile à expliquer: la alert()
intérieure est d'abord exécutée, affiche undefined
(car elle n'a aucun paramètre) et ne renvoie rien. L'alerte externe reçoit la valeur de retour de l'alerte interne - ce qui n'est rien, et affiche également undefined
dans une boîte de message.
Essayez tous les cas sur jsFiddle!
C'est l'approche la plus standard, qui permet d'accéder aux membres d'un objet à l'aide de l'opérateur point (.
). Voici à quoi ressemblerait votre code en notation par points:
alert('a'.toUpperCase())
Beaucoup plus lisible. Alors, quand devrions-nous utiliser la notation par points, et quand devrions-nous utiliser la notation par crochets?
La principale différence entre les deux méthodes est la sémantique. Il y a aussi d'autres détails, mais j'y reviendrai dans un instant. Le plus important est ce que vous voulez réellement faire - une règle de base est que vous utilisez la notation par points pour les champs et les méthodes bien établis d'un objet, et la notation entre crochets lorsque vous utilisez réellement votre objet comme une carte de hachage.
Cet exemple illustre parfaitement la raison pour laquelle cette règle est si importante: puisque le code utilise la notation entre crochets à un endroit où la notation par point aurait été beaucoup plus judicieuse, cela rend le code plus difficile à lire. Et c'est une mauvaise chose, car le code est lu beaucoup plus de fois qu'il n'est écrit .
Dans certains cas, vous devez utiliser notation par crochets même si vous utilisez notation par points étaient plus sensés:
si un membre d'un objet a un nom contenant un ou plusieurs espaces ou tout autre caractère spécial, vous ne pouvez pas utiliser la notation par points: foo.some method()
ne fonctionne pas, mais foo["some method"]()
fonctionne;
si vous avez besoin d'accéder dynamiquement aux membres d'un objet, vous êtes également bloqué par la notation entre crochets;
Exemple:
for(var i = 0; i < 10; ++i) {
foo["method" + i]();
}
En bout de ligne, vous devez utiliser la syntaxe entre crochets lorsque vous utilisez l'objet comme mappage de hachage (foods["burger"].eat()
) et la syntaxe à points lorsque vous utilisez des champs et des méthodes "réels" (enemy.kill()
). Javascript étant un langage dynamique, la ligne de démarcation entre les champs "réels" et les méthodes d'un objet et les "autres" données stockées peut devenir assez floue. Mais tant que vous ne les mélangez pas de manière confuse, tout devrait bien aller.
Passons maintenant au reste de votre question (enfin!: P).
comment puis-je être sûr que la méthode sera toujours un membre de obj
Tu ne peux pas. L'essayer Essayez d'appeler derp
sur une chaîne. Vous obtiendrez une erreur dans les lignes de:
Uncaught TypeError: Object a has no method 'derp'
Il est un peu générique d’appeler TOUTES les méthodes sur n’importe quel objet. Mais cela signifie-t-il que la méthode spécifiée sera déjà un membre implicite de l'objet spécifié?
Oui, dans votre cas cela devrait être. Sinon, vous vous retrouvez avec l'erreur que j'ai mentionnée ci-dessus. Cependant, vous n'avez pas pour utiliser return obj[method]();
dans la fonction callMethod()
. Vous pouvez ajouter votre propre fonctionnalité qui sera ensuite utilisée par la fonction de carte. Voici une méthode codée en dur qui transforme toutes les lettres en majuscules:
function makeCap()
{
return function(obj) {
return obj.toUpperCase();
}
}
var caps2 = map(['a', 'b', 'c'], makeCap()); // ['A','B','C']
console.log(caps2)
Le code dans le tutoriel auquel vous êtes lié utilise fonctions partielles . Ils sont un concept délicat par eux-mêmes. Lire davantage sur ce sujet devrait aider à rendre les choses plus claires que je ne pourrais jamais les rendre.
Remarque: il s'agit du code de la fonction map utilisé par le code de la question, source here .
function map(arr, iterator) {
var narr = [];
for (var i = 0; i < arr.length; i++) narr.Push(iterator(arr[i], i));
return narr;
}
Si vous demandez comment cela fonctionne réellement, c’est ainsi que je le lis. Ok Ceci est une fonction mathématique simple. Pour la comprendre, vous devez consulter la table ascii. Celle-ci attribue une valeur numérique à chaque lettre. Pour la dissimuler, la concurrence utilise simplement un énoncé logique Ainsi, par exemple, If (ChcrValue> 80 && charValue <106) // Ceci est l'ensemble des lettres minuscules, puis charValue = charValue - 38; // la distance entre les ensembles inférieur et supérieur.
c'est aussi simple que cela, je n'ai pas pris la peine de rechercher les valeurs correctes, mais il s'agit en fait de déplacer toutes les lettres minuscules en majuscules.