4
votes

Méthode d'objet utilisant entre la portée lexicale ou cette liaison?

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 commentaires

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 affiche undefined . Voulez-vous renvoyer 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.


3 Réponses :


3
votes

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).


1 commentaires

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.



1
votes

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 )


1 commentaires

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?



0
votes

TL; DR Le mécanisme qui lie la propriété count à la valeur this est liaison implicite

D'abord Si je suis arrivé à une fausse conclusion dans mon explication, voudriez-vous fournir ce que vous êtes d'accord ou pas et partager comment devrais-je penser à ce problème? Je cherche à m'améliorer, merci!

Explication: Lorsque obj.method () est appelé, nous avons une recherche de propriété pour this.count . Si nous inspectons le site d'appel au moment de l'appel de obj.method () , obj est une variable existe dans la portée globale. Les propriétés de obj sont accessibles via la chaîne prototype. Lorsque nous exécutons la recherche de propriété de this.count , nous essayons d'accéder à la propriété dans obj. Lorsque la propriété n'est pas trouvée, nous recherchons la chaîne prototype pour la propriété. obj.count contient / possède la propriété count au moment où nous accédons à la propriété count via la liaison «this».

Causes de mon problème: j'ai mélangé des concepts sur la façon dont cette -binding fonctionne dans arrow-fcns., portée lexicale, et ayant une interprétation trop littérale de «ceci» .

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:

  1. 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
    
  2. 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)
    
  3. 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


4 commentaires

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 , 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")