J'ai du mal à déterminer quel concept explique la raison pour laquelle la valeur de la propriété de l'objet "count" est conservée dans le code ci-dessous.
J'ai lu et examiné le prototype this and object em> de Getify Vous ne connaissez pas JS ainsi que leur section expliquant lexical . Cependant, je ne suis pas en mesure de comprendre mon code ci-dessous. S'agit-il d'une portée lexicale? Ou est-ce une liaison qui permet à la valeur de count d'être conservée?
Voici l'exemple de code:
// 0 // in method 0 // 1 // in method 1 // 2 . . . // 10 // in method 10
J'attends le sortie du premier appel de méthode à obj.method () à la sortie
var obj = { count: 0, method: function() { console.log("in method: " + this.count) return this.count++; }, } // here is where I have issue, when the method is invoked as a function for (var i = 0; i<10; i++) { console.log(obj.method()) // invoked as a function } // I've left this small block in for convenience // I have no trouble with understanding why this block outputs what it outputs for (var i = 0; i<10; i++) { console.log(obj.method) // "gets its value (a reference to a function) and then logs that" from TJ Crowder }
Je n'ai aucun problème avec la sortie. Ma question est encore la suivante: S'agit-il d'une portée lexicale? Ou est-ce une liaison qui permet de conserver la valeur de count?
Merci d'avoir pris le temps de nous aider.
Modifier 1 strong > Avec l'aide de l'article de Tj Crowder ci-dessous, j'ai modifié un extrait de code pour effacer les erreurs, car cela a nui à ma question.
3 Réponses :
Le problème n'a rien à voir avec cette
ou portée. :-) Vous voyez les undefined
car la method
ne renvoie rien, donc l'appelant donne la valeur undefined
, ce que vous journalisation via console.log
. Pour qu'il renvoie la valeur de count
, vous ajoutez un return
:
for (var i = 0; i<10; i++) { console.log(obj.method) // invoked as a property }
qui renvoie la valeur de this.count
telle qu'elle était avant l'incrément (ce qui semble être ce que vous attendez de votre sortie attendue).
Exemple en direct:
.as-console-wrapper { max-height: 100% !important; }
var obj = { count: 0, method: function() { console.log("in method: " + this.count) return this.count++; }, timeOutMethod: function() { // I understand here we explicitly bind this, no problem here setTimeout(function() { console.log(this.count++) }.bind(this), 100) } } // here is where I have issue, when the method is invoked as a function for (var i = 0; i<10; i++) { console.log(obj.method()) // invoked as a function }
Séparément, sur ceci:
method: function() { console.log(this.count) return this.count++; // ^^^^^^ },
qui n'invoque pas méthode
du tout, il obtient juste sa valeur (une référence à une fonction) puis enregistre cela (vous verrez une représentation de la fonction dans la console).
Frappe totale au front! Ne pas fournir la valeur de retour explique certainement pourquoi je devenais indéfini. Je vous remercie! Et séparément, vous avez dissipé une confusion que j'avais pendant un certain temps - j'étais confus sur ce qui se passe lorsqu'une propriété est accédée et connectée à la console et comment l'expliquer avec précision.
Il est contraignant .
La portée est le concept des variables accessibles et des variables que le langage vous cache. En langage machine, toutes les adresses mémoire sont lisibles et inscriptibles, donc en langage machine et dans certains langages d'assemblage, le concept de portée n'existe pas (toutes les variables sont fondamentalement globales). Les langages ultérieurs ont introduit le concept de variables globales vs locales avec l'introduction de fonctions. Ce concept a ensuite évolué vers des fermetures - la possibilité de créer plusieurs instances de champs d'application .
La liaison est le concept de la propriété / attribut appartenant à quel objet. Dans les langages comme Java et C ++ qui implémentent la liaison précoce, le concept de liaison régit simplement la manière dont les méthodes accèdent aux propriétés (généralement cela permet au langage de ne pas avoir besoin d'un mot-clé "this"). Les langages de liaison tardive ont des règles légèrement plus compliquées car la liaison est déterminée au moment de l'exécution au lieu de la compilation. Javascript est non seulement une liaison tardive, mais aussi dynamique - permettant aux programmeurs de changer quel objet cela indique en utilisant des choses comme Function.prototype.call ()
. Et assigner des méthodes d'un objet à un autre objet au moment de l'exécution (par exemple b.foo = a.foo
)
merci pour le contexte historique de l'utilisation de la portée et de la liaison dans les langages de programmation. J'ai relu les ressources sur «ceci» et la portée lexicale et espérons avoir une compréhension correcte de la façon dont cela fonctionne. J'ai posté une explication de ce que je pense être correct en supposant que c'est le mécanisme qui permet d'accéder à la propriété this.count. Voudriez-vous l'examiner et voir sur quoi vous êtes d'accord ou en désaccord et comment mon explication pourrait être améliorée?
Exemples
Voici des exemples de code qui ont été sources de ma confusion.
Mon problème est né de la fusion des conventions sur la façon dont cette
-binding fonctionne dans différents styles de code. J'ai mélangé les concepts suivants:
Comment ce
mot-clé fonctionne dans les fonctions fléchées (c.-à-d. portée lexicale)
function foo(num) { console.log( "foo: " + num ); // keep track of how many times `foo` is called this.count++; } foo.count = 0; var i; for (i=0; i<10; i++) { if (i > 5) { foo( i ); } } // foo: 6 // foo: 7 // foo: 8 // foo: 9 // how many times was `foo` called? console.log( foo.count ); // 0 -- not what we expected
Au lieu de comprendre comment cela
fonctionne réellement, la portée lexicale est utilisée comme solution de contournement. Ici, data.count est incrémenté
function foo() { return data.count++; // the obj property data.count is accessed via global scope } var data = { count: 0, } for (var i = 0; i<10; i++) { foo() } console.log(data.count)
Et enfin, un autre malentendu peut survenir en prenant une interprétation trop littérale de la liaison this
. Je cite que l'extrait provient du livre de Getify
function wait() { setTimeout(() => { console.log(this) // lexically bound }, 100); } wait(); function foo() { // console.log(this) - will output {a: 2} return (a) => { console.log(this.a) // 'this' value is adopted from foo's lexical scope } } var obj = { a: 2 } var obj2 = { a: 3 } // foo is 'this'-bound to obj1 // bar, a reference to the returned arrow-function will also be 'this'-bound to obj1 // the arrow function binding cannot be overridden var bar = foo.call(obj) bar.call(obj2)
À part: merci à @TJ Crowder et @slebetman pour leur aide dans la clarification d'autres malentendus sur la portée et
-binding
Vous devez vraiment étudier le fonctionnement des objets en javascript. foo ()
est une fonction et bien que oui, les fonctions sont également des objets, avoir this.count
à l'intérieur de foo
n'ajoute pas la propriété compter
à la fonction foo
. Au lieu de cela, il tente d'accéder à la propriété count
à partir de l'objet foo
auquel est attaché. Si vous appelez par exemple bar.foo ()
, il accèdera à bar.count
. Si vous appelez foo ()
ce sera la propriété count
de l'objet global, qui si cela était exécuté dans un navigateur serait window.count code >, pas
foo.count
(node.js est plus compliqué)
N'oubliez pas que la liaison et les portées ne sont pas du tout liées les unes aux autres. La portée concerne le masquage des variables (local vs global vs fermeture) - il s'agit du comportement des fonctions. La liaison concerne la structure des objets (pas des fonctions)
En outre, vous devriez vraiment étudier davantage les fermetures (ce que vous semblez penser que vous devez contourner et appeler «portée lexicale»). Si vous comprenez la différence entre les variables globales et locales, il est plus facile de comprendre les fermetures.
Un autre commentaire. Arrêtez de penser au contexte (comme vous l'utilisez ici) lorsque vous voulez expliquer la liaison. Le contexte est UNIQUEMENT IMPLIQUÉ dans les étendues. Vous devez plutôt penser aux chaînes d'héritage. Voyez, comme je l'ai dit, ils ne sont pas du tout liés les uns aux autres. (oui, dans la norme ECMAScript, ils utilisent le mot "contexte" avec deux significations différentes mais le "contexte" d'un objet n'est jamais lié au mot "global")
Peut-être que cela pourrait aider à distinguer les différents appels de journal? Ensuite, vous pouvez voir lequel imprime quelle sortie
Vous ne renvoyez rien de la fonction
method ()
, donc elle afficheundefined
. Voulez-vousrenvoyer ce.compte;
?J'ai modifié l'extrait de code pour exprimer l'intention d'origine de ma question. Le code que j'avais fourni à l'origine était trompeur quant au problème racine et à ce à quoi vous avez répondu, je m'excuse et je vous remercie d'avoir répondu.