28 votes

Pourquoi Javascript ne lie pas mon expression point correctement ?

Je me demande si les méthodes d'abstraction de points (par ex. dog.bark ) lier au moment de l'exécution ou de la compilation. Ma question concerne le code suivant, qui génère une erreur :

(true ? ''.toLowerCase : ''.toUpperCase)()

Mais ce n'est pas le cas de ce qui suit :

true ? ''.toLowerCase() : ''.toUpperCase()

Pourquoi ma chaîne est littérale '' qui ne sont pas résolus dans le premier exemple ?

2 votes

Quand vous le faites dog.bark() , this à l'intérieur de bark pointera vers dog . Mais quand vous le faites fn = dog.bark vous copiez la référence de la fonction. Ainsi, dans fn() , this pointera vers window o undefined en fonction de l'environnement.

0 votes

6 votes

Que voulez-vous dire par "temps de compilation" JavaScript n'est pas un langage compilé. Il n'y a qu'un temps d'exécution.

21voto

PeterMader Points 4626
(true ? ''.toLowerCase : ''.toUpperCase)()

est équivalent à :

String.prototype.toLowerCase.call()
// or:
String.prototype.toLowerCase.call(undefined)

Cependant,

true ? ''.toLowerCase() : ''.toUpperCase()

est équivalent à :

String.prototype.toLowerCase.call('')

Dans les deux cas, le premier argument de call est converti en un objet, que le this sur String.prototype.toLowerCase fera référence.

undefined ne peut pas être converti en objet, mais la chaîne vide peut l'être :

function logThis () { console.log(this); }

logThis.call('');

La console SO snippet ne montre que {} mais c'est en fait la même chose que ce que vous obtenez de new String('') . Pour en savoir plus sur l'enveloppe de la chaîne de caractères, voir MDN .

0 votes

Je sais que c'est assez vieux mais l'endroit où vous dites d'abord "est équivalent à" est complètement faux. Dans l'expression tertiaire, ''.toLowerCase n'appellera pas la fonction. Il vérifiera seulement si toLowerCase sur String.prototype n'est pas faussé. Donc, la première ligne de cette double ligne devrait être supprimée...

0 votes

@marekful Non, l'expression ternaire sera retourner String.prototype.toLowerCase . Les parenthèses à la fin appellent cette fonction.

0 votes

Oui, et c'est le seul appel à la fonction, mais vous dites que cela se traduit par deux appels à la fonction, String.prototype.toLowerCase.call() avec des arguments vides. C'est exactement la même chose dans les deux cas. Aucune différence dans les arguments passés comme vous le suggérez '' contre rien. La démonstration que le littéral de la chaîne vide est transformé en une instance de chaîne a probablement attiré les votes positifs et c'est vraiment bien, mais les gens ont juste détourné le regard du reste au lieu de l'évaluer de manière critique.

7voto

Ahmad Points 4329

Étant donné que ces méthodes s'appliquent à l this et, dans votre exemple, le this es indéfini

Une façon de passer outre este en utilisant bind méthode :

(true ? ''.toLowerCase : ''.toUpperCase).bind('Hello')();

ce qui donnera hello

3 votes

Juste (true ? ''.toLowerCase : ''.toUpperCase).call('Hello') fonctionnerait aussi.

0 votes

@IlmariKaronen, c'est vrai que je voulais que ce soit clair en utilisant bind ici que je remplace le this variable

7voto

JayKuri Points 764

C'est en fait assez simple une fois que vous avez compris comment les méthodes fonctionnent en javascript dans les coulisses.

toUpperCase est une méthode. Il s'agit d'une fonction qui opère sur un objet spécifique... généralement via la fonction this variable.

Javascript est un langage prototypique... ce qui signifie que les fonctions attachées aux objets sont juste des fonctions et peuvent être copiées. Il y a un travail en coulisse qui permet de s'assurer que this est réglé sur la bonne chose lorsque vous appelez une méthode, mais ce travail ne se produit que lorsque vous l'appelez en tant que méthode... comme dans l'exemple suivant obj.method() formulaire.

En d'autres termes : ''.toUpperCase() s'assure que this est défini comme la chaîne de caractères '' quand vous l'appelez.

Quand vous l'appelez comme toUpperCase() this n'a pas de valeur particulière (des environnements différents font des choses différentes avec l'option this dans ce cas)

Ce que fait votre code pourrait être réécrit comme ceci :

var function_to_call;
 if (true) {
    function_to_call = ''.toLowerCase;
 } else {
    function_to_call = ''.toUpperCase;
 }

 function_to_call();

Parce que votre appel de fonction : function_to_call() n'est pas dans le object.method() la syntaxe, la chose qui met this à l'objet correct n'est pas fait, et votre appel de fonction s'exécute avec this n'est pas réglé sur ce que vous voulez.

Comme d'autres personnes l'ont souligné, vous pouvez utiliser func.call(thing_to_make_this) o func.apply() pour attacher la chose correcte à ceci explicitement.

Je trouve beaucoup plus utile d'utiliser .bind() - qui est extrêmement sous-utilisé à mon avis. function_name.bind(this_object) vous donne une nouvelle fonction qui aura toujours this attaché à la bonne chose :

// assuming function_to_call is set
function_that_works = function_to_call.bind(my_object)

function_that_works(); // equivalent to my_object.function_to_call()

et cela signifie que vous pouvez faire circuler la fonction que vous obtenez de la part de bind() comme vous le feriez pour une fonction normale, et elle fonctionnera sur l'objet que vous voulez. Ceci est particulièrement utile dans les callbacks, car vous pouvez créer une fonction anonyme qui est liée à l'objet dans lequel elle a été créée :

// this won't work because when this runs, 'this' doesn't mean what you think
setTimeout(function() { this.display_message('success'); }, 2000);

// this will work, because we have given setTimeout a pre-bound function.
setTimeout(function() { this.display_message('success'); }.bind(this), 2000); 

TL;DR : Vous ne pouvez pas appeler une méthode en tant que fonction et vous attendre à ce que cela fonctionne, parce qu'elle ne sait pas ce qu'elle doit faire. this devrait être. Si vous voulez utiliser cette fonction, vous devez utiliser .call() , .apply() o .bind() pour s'assurer this est correctement défini au moment de l'exécution de la fonction.

J'espère que cela vous aidera.

4 votes

TL;DR : Javascript est fou.

2 votes

La santé mentale étant largement fondée sur des normes au sein de la population, la véracité de cette affirmation dépend de votre appartenance ou non à cette population. Je pense que javascript est très sain et en fait très cool et flexible comme peu d'autres langages le sont... mais je n'attends pas de javascript qu'il soit autre chose que javascript. Si vous venez d'un langage avec des références strictes et/ou qui est orienté classe, vous ne pouvez pas utiliser les mêmes directives. Elles n'ont tout simplement pas de sens. C'est comme si vous vous installiez dans le siège du pilote d'un jet et que vous demandiez où se trouvent le levier de vitesse et la boîte à gants.

4voto

Saravana Points 14313

Parce que quand vous le faites (true ? ''.toLowerCase : ''.toUpperCase)() vous n'appelez pas la fonction qui est liée à une chaîne de caractères. Vous appelez simplement la fonction sans aucun contexte.

Prenons l'exemple suivant :

var obj = {
    objname: "objname",
    getName: function() {
        return this.objname;
    }
}

Quand vous le suivez avec obj.getName() il renvoie correctement la valeur, mais lorsque vous faites quelque chose comme ceci :

var fn = obj.getName
fn() // returns undefined because `fn` is not bound to `obj`

0voto

Dmitry Points 68

Dans votre premier exemple, le toLowerCase est détachée de son contexte (l'objet chaîne vide), puis elle est invoquée. Puisque vous ne rattachez pas la fonction à quoi que ce soit qu'elle a undefined comme son contexte.

Ce comportement existe pour permettre la réutilisation du code par le biais de mix-ins :

var obj1 = {
   name: "obj1",
   getName: function() { return this.name; }
}

var obj2 = {
   name: "obj2",
}

obj2.getName = obj1.getName //now obj2 has the getName method with correct context

console.log(obj2.getName())

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X