Portée Lexique / fermetures dans javaScript

Je comprends que les fonctions dans 'js' ont une portée lexicale (c'est-à-dire que les fonctions créent leur environnement (portée) lorsqu'elles sont définies non lorsqu'elles sont exécutées.)

function f1() { var a = 1; f2(); } function f2() { return a; } f1(); // a is not defined 

Lorsque je cours juste 'f ()', il renvoie la fonction interne. Ce que je reçois, c'est ce que le «retour» fait!

 function f() { var b = "barb"; return function() { return b; } } console.log(b); //ReferenceError: b is not defined 

Pourquoi obtenez-vous 'ReferenceError: b n'est pas défini?' Mais la fonction interne ci-dessus n'a-t-elle pas accès à son espace, à l'espace de f (). Étant que «b» est retourné à l'espace global, la console.log () ne fonctionnerait-elle pas?

Cependant, lorsque j'affecte 'f ()' à une nouvelle variable et l'exécute:

  var x = f(); x();// "barb" console.log(b); //ReferenceError: b is not defined 

Cela renvoie 'b' qui est "barb", mais lorsque vous exécutez console.log (), vous obtiendrez 'ReferenceError:' b 'n'est pas défini'; N'est-ce pas «b» dans la portée mondiale maintenant depuis qu'il a été retourné? Alors, pourquoi '' x () 'a-t-il également retourné la fonction interne comme' f () 'a-t-il?

    Vous, mon ami, sont complètement confus. Votre première déclaration elle-même est fausse:

    Les fonctions créent leur environnement (portée) lorsqu'elles sont définies non pas lorsqu'elles sont exécutées

    En fait, c'est le contraire. La définition d'une fonction ne crée pas de portée. L'appel d'une fonction crée une portée.

    Quelle est la portée?

    Pour le dire simplement, une portée est la durée de vie d'une variable. Vous voyez, chaque variable est née, vit et décède. Le début d'une portée marque le moment où la variable est née et la fin de la portée marque le moment où elle décède.

    Au début, il n'y a qu'une seule portée (appelée portée du programme ou portée globale). Les variables créées dans cette zone ne meurent que lorsque le programme se termine. Ils sont appelés variables globales.

    Par exemple, considérez ce programme (ce n'est pas JavaScript):

     x = 10 // global variable x { // beginning of a scope x = 20 // local variable x print(x) // 20 } // end of the scope print(x) // 10 

    Ici, nous avons créé une variable globale appelée x . Ensuite, nous avons créé une portée de bloc. Dans ce cadre de bloc, nous avons créé une variable locale x . Puisque les variables locales occultent des variables globales lorsque nous imprimons x nous obtenons 20 . Retour dans la portée globale lorsque nous imprimons x nous obtenons 10 (le x local est maintenant mort).

    Scènes de blocs et champs de fonctions

    Maintenant, il existe deux principaux types d'étendues dans la programmation – étendues de blocs et champs de fonctions.

    La portée dans l'exemple précédent était une portée de bloc. C'est juste un bloc de code. D'où le nom. Les étendues de blocs sont immédiatement exécutées.

    Les écrans de fonction d'autre part sont des modèles d'étendues de blocs. Comme son nom l'indique, un périmètre de fonction appartient à une fonction. Cependant, plus précisément, il appartient à un appel de fonction. Les champs fonctionnels n'existent pas jusqu'à ce qu'une fonction soit appelée. Par exemple:

     var x = 10 function inc(x) { print(x + 1); } inc(3); // 4 print(x); // 10 inc(7); // 8 

    Comme vous pouvez le constater chaque fois que vous appelez une fonction, une nouvelle portée est créée. C'est la raison pour laquelle vous obtenez les résultats 4 , 10 et 8 .

    JavaScript n'a que la portée de la fonction. Il n'a pas d'étendue de bloc. Par conséquent, si vous voulez créer une portée de bloc, vous devez créer une fonction et l'exécuter immédiatement:

     var x = 10; // global variable x (function () { // beginning of a scope var x = 20; // local variable x print(x); // 20 }()); // end of the scope print(x); // 10 

    Ce modèle s'appelle une expression de fonction appelée immédiatement (IIFE) .

    Elargissements Lexiques et Scopes Dynamiques

    Les étendues de fonctions peuvent encore être de deux types: lexical et dynamique. Vous voyez, dans une fonction, il existe deux types de variables:

    1. Variables gratuites
    2. Variables liées

    Les variables déclarées dans un champ d'application sont liées à cette portée. Les variables non déclarées dans un périmètre sont gratuites. Ces variables gratuites appartiennent à un autre domaine, mais lequel?

    Portée Lexique

    Dans les variables libres de portée lexicale, elles doivent appartenir à une portée parentale. Par exemple:

     function add(x) { // template of a new scope, x is bound in this scope return function (y) { // template of a new scope, x is free, y is bound return x + y; // x resolves to the parent scope }; } var add10 = add(10); // create a new scope for x and return a function print(add10(20)); // create a new scope for y and return x + y 

    JavaScript, comme la plupart des langages de programmation, a une portée lexicale.

    Portée dynamique

    Contrairement à la portée du lexical, les variables libres de portée dynamique doivent appartenir à la portée d'appel (la portée de la fonction d'appel). Par exemple (ce n'est pas JS – il n'a pas d'étendues dynamiques):

     function add(y) { // template of a new scope, y is bound, x is free return x + y; // x resolves to the calling scope } function add10(y) { // template of a new scope, bind y var x = 10; // bind x return add(y); // add x and y } print(add10(20)); // calling add10 creates a new scope (the calling scope) // the x in add resolves to 10 because the x in add10 is 10 

    C'est tout. Simple à droite?

    Le problème

    Le problème avec votre premier programme est que JavaScript n'a pas de portée dynamique. Il n'y a que la portée lexicale. Vous voyez l'erreur?

     function f1() { var a = 1; f2(); } function f2() { return a; } f1(); // a is not defined (obviously - f2 can't access the `a` inside f1) 

    Votre deuxième programme est un très gros désordre:

     function f() { var b = "barb"; return function() { return b; } } console.log(b); //ReferenceError: b is not defined 

    Voici les erreurs:

    1. Vous n'avez jamais appelé f . Par conséquent, la variable b n'est jamais créée.
    2. Même si vous appelez f la variable b serait locale à f .

    C'est ce que vous devez faire:

     function f() { var b = "barb"; return function() { return b; } } var x = f(); console.log(x()); 

    Lorsque vous appelez x il retourne b . Cependant, cela ne rend pas b global. Pour rendre global, vous devez faire ceci:

     var x = f(); var b = x(); console.log(b); 

    J'espère que cela vous a aidé à comprendre les champs et les fonctions.

    Vous obtenez "ReferenceError: b n'est pas défini" car "b" n'est pas défini où votre appel console.log() est. Il y a un "b" à l' intérieur de cette fonction, mais pas à l'extérieur. Votre affirmation selon laquelle "b est retourné à l'espace global" est fausse.

    Lorsque vous appelez la fonction retournée par votre fonction "f ()", cela renverra une copie de la valeur référencée par cette variable de fermeture "b". Dans ce cas, "b" sera toujours cette chaîne, donc la fonction renvoie cette chaîne. Il n'entend pas que le symbole "b" devienne une variable globale.

    Mais la fonction interne ci-dessus n'a-t-elle pas accès à son espace, l'espace de f (), etc.

    Oui, il l'a fait. Il accède à la variable b et renvoie sa valeur à partir de la fonction .

    Étant donné que «b» est retourné à l'espace mondial

    Non. Renvoyer une valeur à partir d'une fonction n'est pas "rendre une variable disponible dans la portée de l'appelant". Appeler la fonction (avec f() ) est une expression dont le résultat est la valeur que la fonction a renvoyée (dans votre cas, l'objet de fonction non nommé). Cette valeur peut ensuite être attribuée quelque part (à x ), une propriété de celle-ci peut être accessible ou elle peut être rejetée.

    La variable b reste cependant privée dans le champ d'application où elle a été déclarée. Ce n'est pas [obtenir] défini dans le champ d'application où vous appelez console.log , c'est pourquoi vous obtenez une erreur.

    Ce que vous voulez semble être

     var x = f(); var b = x(); // declare new variable b here, assign the returned value console.log( b ); // logs "barb" 
      function f1() { var a = 1; f2(); } function f2() { return a; } f1(); // a is not defined 
    1. F2 (); Ne connaît pas le a, parce que vous ne l'avez jamais passé 'a' (ce qui est la portée sont créés lorsque les fonctions sont définies). La fonction de recherche f2 () aurait pu accéder a si elle a été définie dans f1 (); [Les fonctions peuvent accéder aux variables dans le même champ dans lequel elles sont "DÉFINIES" et NON "APPELÉES"]

       function f() { var b = "barb"; return function(){ return b; } } console.log(b); 
    2. Tout d'abord, vous devez appeler f (); Après l'exécution de f (); Il renverrait une autre fonction qui doit être exécutée. c'est à dire

       var a=f(); a(); 

      Il en résulterait en "barb", dans ce cas vous renvoyez une fonction non pas la var b;

       function f() { var b = "barb"; return b; }; console.log(f()); 

      Cela impressionnerait la barre sur l'écran