[Problème 764] Vous ne comprenez pas JS: qu'est-ce que c'est?

image
Préface


Vers la fin de l'année, je pense que le prochain article est exactement ce dont vous avez besoin. Cet article est traduit et autorisé à partager par l'auteur de la colonne de la classe de lecture du matin @HetfieldJoe.

ps: Si vous voulez voir le code, vous pouvez cliquer sur l'image pour voir


Le texte commence ici ~


C'est vous ne comprenez pas JS: ceci et prototype d'objet Chapitre 1: Qu'est-ce que c'est?


L'un des mécanismes les plus déroutants de JavaScript est le mot-clé this. C'est un mot-clé identifiant spécial qui est automatiquement défini dans la portée de chaque fonction, mais même certains développeurs expérimentés ne savent pas trop sur quoi il pointe.


Toute technologie suffisamment avancée n'est pas différente de la magie. - Arthur C. Clarke


Ce mécanisme de JavaScript n'est en fait pas si avancé, mais les développeurs citent toujours cette phrase dans leur cerveau pour exprimer «complexité» et «confusion». Il ne fait aucun doute que s'il n'y a pas de compréhension claire, cela peut être dans votre confusion. Cela semble être une magie totale.


Remarque: Le mot «ceci» est un pronom très courant dans la discussion générale. Par conséquent, en particulier dans les discussions orales, il est difficile de déterminer si nous utilisons «ceci» comme pronom ou comme véritable identificateur de mot clé. Pour plus de clarté, j'utiliserai toujours ceci pour représenter des mots clés spéciaux, et dans d'autres cas, j'utiliserai "ceci" ou ceci ou ceci.


Pourquoi utiliser ça?

Si ce mécanisme est si déroutant pour ces développeurs JavaScript expérimentés, alors quelqu'un demandera pourquoi ce mécanisme est utile? Cela ne cause-t-il pas plus de problèmes que d'avantages? Avant d'expliquer en quoi il est utile, nous devons d'abord examiner pourquoi il est utile.


Essayons de montrer la motivation et le but de ceci:

image


Si le fonctionnement de cet extrait de code vous déroute, ne vous inquiétez pas! Nous vous l'expliquerons bientôt. Mettez simplement ces questions de côté afin que nous puissions explorer plus clairement pourquoi.


Cet extrait de code permet aux fonctions identifier () et speak () de réutiliser plusieurs objets d'environnement (moi et vous) au lieu de définir des versions distinctes des fonctions pour chaque objet.


Contrairement à l'utilisation de ceci, vous pouvez passer explicitement l'objet d'environnement à identifier () et speak ().

image.png


Cependant, ce mécanisme offre un moyen plus élégant de "passer" implicitement une référence d'objet, ce qui permet une conception d'API plus propre et une réutilisation plus facile.


Plus votre modèle d'utilisation est complexe, plus vous verrez clairement: passer l'environnement d'exécution en tant que paramètre explicite est généralement plus compliqué que passer cet environnement d'exécution. En explorant les objets et les prototypes, vous verrez à quel point un ensemble de fonctions peut faire référence automatiquement aux objets d'environnement d'exécution appropriés.


confus

Nous commencerons bientôt à expliquer comment cela fonctionne réellement, mais nous devons d'abord abandonner certains malentendus - cela ne fonctionne pas réellement.


La confusion survient lorsque les développeurs pensent trop littéralement au nom «ceci». Cela produit généralement deux types de conjectures, mais les deux sont fausses.


lui-même

La première tendance commune est de penser que cela renvoie à la fonction elle-même. Au moins, c'est une supposition grammaticalement raisonnable.


Pourquoi voulez-vous vous référer à lui-même dans la fonction? La raison la plus courante est une situation telle que la récursivité (qui s’invoque à l’intérieur d’une fonction), ou un gestionnaire d’événements qui se dissocie la première fois qu’il est appelé.


Les développeurs qui sont nouveaux dans le mécanisme JS pensent généralement que l'utilisation d'une fonction comme objet (toutes les fonctions en JavaScript sont des objets!) Vous permet de stocker l'état (les valeurs dans les propriétés) entre les appels de méthode. Ceci est certainement possible et a une utilité limitée, mais le reste de ce livre expliquera de nombreux autres modèles qui fournissent de meilleurs endroits pour stocker l'état que les objets de fonction.


Dans un moment, nous explorerons un modèle pour montrer comment cela empêche une fonction d'obtenir une référence à elle-même comme nous pourrions le supposer.


Considérez le code suivant, nous essayons de suivre combien de fois la fonction (foo) est appelée:

image.png


foo.count est toujours 0, même si les quatre instructions console.log nous indiquent clairement que foo (..) a été appelé quatre fois. Cet échec vient d'une interprétation trop littérale de la signification de ceci (dans this.count ++).


Lorsque le code exécute foo.count = 0, il ajoute un attribut count à l'objet fonction foo. Mais pour la référence this.count à l'intérieur de la fonction, cela ne pointe pas du tout vers l'objet fonction. Même si le nom d'attribut est le même, l'objet racine est différent, ce qui crée de la confusion.


Remarque: Un développeur responsable doit poser une question ici: "Si l'attribut count que j'incrémente n'est pas celui que je pensais, quel compte a été incrémenté par moi?". En fait, s'il creuse plus profondément, il découvrira qu'il a accidentellement créé un nombre de variables globales (le chapitre 2 explique comment cela se produit), et sa valeur actuelle est NaN. Bien sûr, une fois qu'il découvre ce résultat inhabituel, il aura un tas d'autres questions: "Comment est-il global? Pourquoi est-ce NaN au lieu d'une valeur de comptage correcte?". (Voir le chapitre 2)


Au lieu de s'arrêter ici pour explorer pourquoi cette référence ne semble pas fonctionner comme prévu, et pour répondre à ces questions nettes et importantes, de nombreux développeurs évitent tout simplement ce problème et se tournent vers d'autres solutions alternatives, telles que la création d'un autre. Un objet pour contenir l'attribut count:

image.png


Bien que cette méthode «résout» le problème, malheureusement, elle ignore simplement le vrai problème - faute de comprendre la signification de ceci et comment cela fonctionne - au lieu de cela se retire dans le confort d'un mécanisme plus familier. Zone: portée lexicale.


Remarque: la portée lexicale est un mécanisme parfait et utile, je n'essaye pas de la minimiser en aucune façon (voir "Portée et fermetures" dans cette série). Mais sur la question de savoir comment l'utiliser, vous comptez toujours sur des suppositions, et vous faites généralement des erreurs.Ce n'est pas une bonne raison de revenir à la portée lexicale et de ne jamais apprendre pourquoi cela ne fonctionne pas avec vous.


Pour se référer à lui-même à partir d'un objet fonction, il ne suffit généralement pas de passer cela. Vous devez généralement obtenir une référence à l'objet fonction via un identifiant lexical (variable) pointant vers lui.


Considérez ces deux fonctions:

image.png


La première fonction, appelée «fonction nommée», foo est une référence qui peut être utilisée pour s'y référer.


Mais dans le deuxième exemple, la fonction de rappel passée à setTimeout (..) n'a pas d'identificateur de nom (elle est donc appelée "fonction anonyme"), il n'y a donc pas de moyen approprié de faire référence à l'objet fonction lui-même.


Remarque: Il y a une fonction ancienne mais maintenant obsolète dans la fonction, et la référence froncement de sourcils arguments.callee pointe également vers l'objet fonction de la fonction en cours d'exécution. Cette référence est généralement le seul moyen pour une fonction anonyme d'accéder à l'objet fonction en elle-même. Cependant, le meilleur moyen est d'éviter complètement les fonctions anonymes, du moins pour les fonctions qui nécessitent un auto-référencement, et d'utiliser des fonctions nommées (expressions). arguments.callee est obsolète et ne doit plus être utilisé.


Pour notre exemple actuel, une autre solution utile consiste à utiliser l'identifiant foo comme référence à l'objet fonction partout au lieu de cela du tout:

image.png


Cependant, cette méthode évite de la même manière la vraie compréhension de ceci, et repose complètement sur la portée lexicale de la variable foo.


Une autre façon de résoudre le problème est de forcer cela à pointer vers l'objet de la fonction foo:

image


Au lieu d'éviter cela, nous l'acceptons. Nous vous expliquerons plus en détail comment fonctionne cette technique, donc si vous êtes encore un peu confus, ne vous inquiétez pas!


Sa portée

Le deuxième malentendu courant de la signification de ceci est qu'il indique en quelque sorte la portée de la fonction. C'est une question délicate, car dans un sens, elle a une partie correcte, et dans un autre sens, elle est sérieusement trompeuse.


Pour être clair, cela n'indique en aucun cas la portée lexicale de la fonction. La portée semble être un objet avec tous les identificateurs disponibles comme attributs, ce qui est vrai en interne. Mais le code JavasScript ne peut pas accéder à la portée "objet". C'est la mise en œuvre interne du moteur.


Considérez le code suivant, qui tente (a échoué) de franchir cette limite, en l'utilisant pour faire référence implicitement à la portée lexicale de la fonction:

image


Il y a plus d'une erreur dans cet extrait de code. Bien que cela semble être un désordre délibéré, le code que vous voyez est extrait du code réel échangé dans la communauté du forum d'aide public. Il est difficile d'imaginer à quel point la conjecture à ce sujet est trompeuse.


Tout d'abord, essayez de référencer la fonction bar () via this.bar (). On peut presque dire que cela a fonctionné, nous vous expliquerons comment cela fonctionne dans un moment. La manière la plus naturelle d'appeler bar () est d'omettre le début de ceci et de ne faire qu'une référence lexicale à l'identifiant.


Cependant, le développeur qui a écrit ce code a essayé de l'utiliser pour construire un pont entre la portée lexicale de foo () et bar (), afin que bar () puisse accéder à la variable a dans la portée interne de foo (). Un tel pont est impossible. Vous ne pouvez pas utiliser cette référence pour trouver des choses dans une portée lexicale. C'est impossible.


Chaque fois que vous avez l'impression d'essayer de l'utiliser pour une requête de portée lexicale, rappelez-vous: il n'y a pas de pont.


Qu'est-ce que c'est?

Nous avons répertorié diverses hypothèses incorrectes, concentrons-nous maintenant sur le fonctionnement réel de ce mécanisme.


Comme nous l'avons dit précédemment, cela ne lie pas à l'écriture, mais à l'exécution. Cela dépend du contexte de l'appel de fonction. Cette liaison n'a rien à voir avec l'emplacement de la déclaration de fonction, mais avec la façon dont la fonction est appelée.


Lorsqu'une fonction est appelée, un enregistrement d'activité est créé, également appelé environnement d'exécution. Cet enregistrement contient des informations sur l'endroit où la fonction est appelée (pile d'appels), comment la fonction est appelée et quels paramètres sont passés. L'un des attributs de cet enregistrement est la référence this qui sera utilisée lors de l'exécution de la fonction.


la revue

Pour les développeurs JavaScript qui n'ont pas pris le temps d'apprendre comment fonctionne ce mécanisme de liaison, cette liaison a toujours été une source de confusion. Les devinettes, essais et erreurs, ou copier-coller aveuglément à partir de réponses Stack Overflow ne sont pas des moyens efficaces ou corrects d'utiliser cet important mécanisme.


Pour apprendre cela, vous devez d'abord savoir ce que ce n'est pas, quel que soit le type de conjecture ou de malentendu qui vous induit en erreur. ce n'est ni une référence à la fonction elle-même ni une référence à la portée lexicale de la fonction.


Il s'agit en fait d'une liaison établie lorsque la fonction est appelée, et ce sur quoi elle pointe est complètement déterminé par le point d'appel où la fonction est appelée.


Je suppose que tu aimes

Origine blog.51cto.com/15080028/2595046
conseillé
Classement