Recherche…


Remarques

Scope est le contexte dans lequel les variables vivent et peut être accédé par un autre code dans la même portée. JavaScript pouvant être largement utilisé comme langage de programmation fonctionnel, il est important de connaître la portée des variables et des fonctions, car cela permet d'éviter les bogues et les comportements inattendus à l'exécution.

Différence entre var et let

(Remarque: tous les exemples utilisant let sont également valables pour const )

var est disponible dans toutes les versions de JavaScript, alors que let et const font partie d'ECMAScript 6 et sont uniquement disponibles dans certains navigateurs récents .

var est portée à la fonction contenant ou à l'espace global, selon qu'elle est déclarée:

var x = 4; // global scope

function DoThings() {
    var x = 7; // function scope
    console.log(x);
}

console.log(x); // >> 4
DoThings();     // >> 7
console.log(x); // >> 4

Cela signifie qu'il "échappe" if instructions et toutes les constructions de blocs similaires:

var x = 4;
if (true) {
    var x = 7;
}
console.log(x); // >> 7

for (var i = 0; i < 4; i++) {
    var j = 10;
}
console.log(i); // >> 4
console.log(j); // >> 10

À titre de comparaison, let champ d’attente:

let x = 4;

if (true) {
    let x = 7;
    console.log(x); // >> 7
}

console.log(x); // >> 4

for (let i = 0; i < 4; i++) {
    let j = 10;
}
console.log(i); // >> "ReferenceError: i is not defined"
console.log(j); // >> "ReferenceError: j is not defined"

Notez que i et j ne sont déclarés que dans la boucle for et sont donc non déclarés en dehors de celle-ci.

Il existe plusieurs autres différences cruciales:

Déclaration de variable globale

Dans la portée supérieure (en dehors des fonctions et des blocs), les déclarations var placent un élément dans l'objet global. let ne pas:

var x = 4;
let y = 7;

console.log(this.x); // >> 4
console.log(this.y); // >> undefined

Re-déclaration

Déclarer une variable deux fois en utilisant var ne produit pas d'erreur (même si cela équivaut à la déclarer une fois):

var x = 4;
var x = 7;

Avec let , cela produit une erreur:

let x = 4;
let x = 7;

TypeError: l'identifiant x a déjà été déclaré

La même chose est vraie lorsque y est déclaré avec var :

var y = 4;
let y = 7;

TypeError: l'identifiant y a déjà été déclaré

Cependant les variables déclarées avec let peuvent être réutilisées (non re-déclarées) dans un bloc imbriqué

let i = 5;    
{
   let i = 6;
   console.log(i); // >> 6
}
console.log(i); // >> 5

Au sein du bloc l'extérieur i est inaccessible, mais si le bloc a une à l' intérieur let déclaration pour i , l'extérieur i ne peux pas être accessible et jetterai un ReferenceError si elle est utilisée avant la seconde est déclarée.

let i = 5;
{
    i = 6;  // outer i is unavailable within the Temporal Dead Zone
    let i;
}

ReferenceError: i n'est pas défini

Levage

Les variables déclarées à la fois avec var et let sont hissées . La différence est qu’une variable déclarée avec var peut être référencée avant sa propre affectation, car elle est automatiquement attribuée (avec une valeur undefined ), mais let peut pas - elle exige spécifiquement que la variable soit déclarée avant d’être invoquée:

console.log(x); // >> undefined
console.log(y); // >> "ReferenceError: `y` is not defined"
//OR >> "ReferenceError: can't access lexical declaration `y` before initialization"
var x = 4;
let y = 7;

La zone située entre le début d'un bloc et une déclaration let ou const est appelée zone morte temporelle , et toute référence à la variable dans cette zone provoquera une ReferenceError . Cela se produit même si la variable est affectée avant d'être déclarée :

y=7; // >> "ReferenceError: `y` is not defined"
let y;

En mode non strict, l' affectation d'une valeur à une variable sans déclaration déclare automatiquement la variable dans la portée globale . Dans ce cas, au lieu de y étant automatiquement déclaré dans la portée globale, let le nom de la variable ( y ) en réserve et n'autorisez aucun accès ou affectation avant la ligne où il est déclaré / initialisé.

Fermetures

Lorsqu'une fonction est déclarée, les variables dans le contexte de sa déclaration sont capturées dans sa portée. Par exemple, dans le code ci-dessous, la variable x est liée à une valeur dans la portée externe, puis la référence à x est capturée dans le contexte de la bar :

var x = 4; // declaration in outer scope

function bar() {
    console.log(x); // outer scope is captured on declaration
}

bar(); // prints 4 to console

Sortie de l'échantillon: 4

Ce concept de portée "capturer" est intéressant car nous pouvons utiliser et modifier des variables depuis une étendue externe même après la fin de la portée externe. Par exemple, prenez en compte les éléments suivants:

function foo() {
    var x = 4; // declaration in outer scope

    function bar() {
        console.log(x); // outer scope is captured on declaration
    }

    return bar;
    
    // x goes out of scope after foo returns
}

var barWithX = foo();
barWithX(); // we can still access x

Sortie de l'échantillon: 4

Dans l'exemple ci-dessus, lorsque foo est appelé, son contexte est capturé dans la bar fonctions. Donc, même après son retour, la bar peut toujours accéder à la variable x et la modifier. La fonction foo , dont le contexte est capturé dans une autre fonction, est considérée comme une fermeture .

Données privées

Cela nous permet de faire des choses intéressantes, telles que la définition de variables "privées" visibles uniquement pour une fonction ou un ensemble de fonctions spécifique. Un exemple artificiel (mais populaire):

function makeCounter() {
    var counter = 0;

    return {
        value: function () {
            return counter;
        },
        increment: function () {
            counter++;
        }
    };
}

var a = makeCounter();
var b = makeCounter();

a.increment();

console.log(a.value());
console.log(b.value());

Sortie de l'échantillon:

1
0

Lorsque makeCounter() est appelé, un instantané du contexte de cette fonction est enregistré. Tout le code de makeCounter() utilisera cet instantané dans leur exécution. Deux appels de makeCounter() créeront donc deux instantanés différents, avec leur propre copie du counter .

Expressions de fonction immédiatement appelées (IIFE)

Les fermetures sont également utilisées pour empêcher la pollution globale des espaces de noms, souvent grâce à l'utilisation d'expressions de fonctions invoquées immédiatement.

Les expressions de fonction invoquées immédiatement (ou, peut-être de manière plus intuitive, des fonctions anonymes auto-exécutables ) sont essentiellement des fermetures appelées juste après la déclaration. L’idée générale de l’IECF est d’invoquer l’effet secondaire de la création d’un contexte distinct accessible uniquement au code de l’IECF.

Supposons que nous voulions pouvoir référencer jQuery avec $ . Considérons la méthode naïve, sans utiliser un IIFE:

var $ = jQuery;
// we've just polluted the global namespace by assigning window.$ to jQuery

Dans l'exemple suivant, un IIFE est utilisé pour garantir que $ est lié à jQuery uniquement dans le contexte créé par la fermeture:

(function ($) {
    // $ is assigned to jQuery here
})(jQuery);
// but window.$ binding doesn't exist, so no pollution

Voir la réponse canonique sur Stackoverflow pour plus d'informations sur les fermetures.

Levage

Qu'est-ce que le levage?

Le levage est un mécanisme qui déplace toutes les déclarations de variables et de fonctions au sommet de leur portée. Cependant, les affectations de variables se produisent toujours là où elles étaient à l'origine.

Par exemple, considérez le code suivant:

console.log(foo);  // → undefined
var foo = 42;
console.log(foo);  // → 42

Le code ci-dessus est le même que:

var foo;             // → Hoisted variable declaration
console.log(foo);    // → undefined
foo = 42;            // → variable assignment remains in the same place
console.log(foo);    // → 42

Notez que le undefined ci-dessus n'est pas le même que celui not defined résultant de l'exécution:

console.log(foo);    // → foo is not defined 

Un principe similaire s'applique aux fonctions. Lorsque des fonctions sont affectées à une variable (c'est-à-dire une expression de fonction ), la déclaration de variable est hissée pendant que l'affectation reste au même endroit. Les deux extraits de code suivants sont équivalents.

console.log(foo(2, 3));     // → foo is not a function

var foo = function(a, b) {
    return a * b;
}
var foo;
console.log(foo(2, 3));     // → foo is not a function
foo = function(a, b) {
    return a * b;
}

Lors de la déclaration des instructions de fonction , un scénario différent se produit. Contrairement aux instructions de fonction, les déclarations de fonction sont placées au sommet de leur portée. Considérez le code suivant:

console.log(foo(2, 3));  // → 6
function foo(a, b) {
    return a * b;
}

Le code ci-dessus est identique à l'extrait de code suivant en raison du levage:

function foo(a, b) {
    return a * b;
}

console.log(foo(2, 3));  // → 6

Voici quelques exemples de ce qui est ou non:

// Valid code:
foo();

function foo() {}

// Invalid code:
bar();                     // → TypeError: bar is not a function
var bar = function () {};


// Valid code:
foo();
function foo() {
    bar();
}
function bar() {}


// Invalid code:
foo();
function foo() {
    bar();                // → TypeError: bar is not a function
}
var bar = function () {};


// (E) valid:
function foo() {
    bar();
}
var bar = function(){};
foo();

Limites du levage

L'initialisation d'une variable ne peut pas être levée ou In simple JavaScript déclenche des déclarations non initialisées.

Par exemple: Les scripts ci-dessous donneront des sorties différentes.

var x = 2; 
var y = 4; 
alert(x + y);

Cela vous donnera une sortie de 6. Mais cela ...

var x = 2; 
alert(x + y);
var y = 4; 

Cela vous donnera une sortie de NaN. Puisque nous initialisons la valeur de y, le levage de JavaScript ne se produit pas, donc la valeur de y sera indéfinie. Le JavaScript considérera que y n'est pas encore déclaré.

Le deuxième exemple est donc le même que ci-dessous.

var x = 2; 
var y;
alert(x + y);
y = 4; 

Cela vous donnera une sortie de NaN.

entrer la description de l'image ici

Utiliser let in loops au lieu de var (exemple des gestionnaires de clic)

Disons que nous devons ajouter un bouton pour chaque élément du tableau loadedData (par exemple, chaque bouton doit être un curseur affichant les données; pour des raisons de simplicité, nous allons simplement alerter un message). On peut essayer quelque chose comme ça:

for(var i = 0; i < loadedData.length; i++)
    jQuery("#container").append("<a class='button'>"+loadedData[i].label+"</a>")
        .children().last() // now let's attach a handler to the button which is a child
        .on("click",function() { alert(loadedData[i].content); });

Mais au lieu d'alerter, chaque bouton provoquera la

TypeError: loadedData [i] n'est pas défini

Erreur. C'est parce que la portée de i est la portée globale (ou une portée de fonction) et après la boucle, i == 3 . Ce dont nous avons besoin, ce n'est pas de "nous souvenir de l'état de i ". Cela peut être fait en utilisant let :

for(let i = 0; i < loadedData.length; i++)
    jQuery("#container").append("<a class='button'>"+loadedData[i].label+"</a>")
        .children().last() // now let's attach a handler to the button which is a child
        .on("click",function() { alert(loadedData[i].content); });

Un exemple de loadedData à tester avec ce code:

    var loadedData = [
        { label:"apple",      content:"green and round" },
        { label:"blackberry", content:"small black or blue" },
        { label:"pineapple",  content:"weird stuff.. difficult to explain the shape" }
    ];

Un violon pour illustrer cela

Invocation de méthode

Invoquer une fonction comme méthode d'un objet, la valeur de this objet sera cet objet.

var obj = {
    name: "Foo",
    print: function () {
        console.log(this.name)
    }
}

Nous pouvons maintenant appeler print comme méthode d'obj. this sera obj

obj.print();

Cela va donc se connecter:

Foo

Invocation anonyme

En appelant une fonction en tant que fonction anonyme, this sera l'objet global ( self dans le navigateur).

function func() {
    return this;
}

func() === window; // true
5

Dans le mode strict d'ECMAScript 5 , this ne sera pas undefined si la fonction est invoquée de manière anonyme.

(function () {
    "use strict";
    func();
}())

Cela va sortir

undefined

Invocation du constructeur

Lorsqu'une fonction est appelée en tant que constructeur avec le new mot this clé, this prend la valeur de l'objet en cours de construction

function Obj(name) {
    this.name = name;
}

var obj = new Obj("Foo");

console.log(obj);

Cela se connectera

{name: "Foo"}

Invocation de la fonction flèche

6

Lors de l' utilisation des fonctions de direction this prend la valeur du contexte d'exécution d'enceinte est this (à savoir, this dans des fonctions de direction a une portée lexicale plutôt que la portée dynamique d' habitude). En code global (code qui n'appartient à aucune fonction), ce serait l'objet global. Et cela se maintient, même si vous invoquez la fonction déclarée avec la notation en flèche de l'une des autres méthodes décrites ici.

var globalThis = this; //"window" in a browser, or "global" in Node.js

var foo = (() => this);           

console.log(foo() === globalThis);          //true

var obj = { name: "Foo" };
console.log(foo.call(obj) === globalThis);  //true

Voyez comment this hérite du contexte plutôt que de faire référence à l'objet sur lequel la méthode a été appelée.

var globalThis = this;

var obj = {
    withoutArrow: function() {
        return this;
    },
    withArrow: () => this
};

console.log(obj.withoutArrow() === obj);      //true
console.log(obj.withArrow() === globalThis);  //true

var fn = obj.withoutArrow; //no longer calling withoutArrow as a method
var fn2 = obj.withArrow;
console.log(fn() === globalThis);             //true
console.log(fn2() === globalThis);            //true

Appliquer et appeler la syntaxe et l'appel.

Les méthodes apply et call de chaque fonction lui permettent de fournir une valeur personnalisée pour this .

function print() {
    console.log(this.toPrint);
}

print.apply({ toPrint: "Foo" }); // >> "Foo"
print.call({ toPrint: "Foo" }); // >> "Foo"

Vous remarquerez peut-être que la syntaxe des deux invocations utilisées ci-dessus est la même. c'est-à-dire que la signature est similaire.

Mais il y a une petite différence dans leur utilisation, étant donné que nous traitons des fonctions et modifions leurs étendues, nous devons toujours conserver les arguments originaux transmis à la fonction. Les deux apply et call support transmettent des arguments à la fonction cible comme suit:

function speak() {
    var sentences = Array.prototype.slice.call(arguments);
    console.log(this.name+": "+sentences);
}
var person = { name: "Sunny" };
speak.apply(person, ["I", "Code", "Startups"]); // >> "Sunny: I Code Startups"
speak.call(person, "I", "<3", "Javascript"); // >> "Sunny: I <3 Javascript"

Notez que apply vous permet de passer un objet Array ou un objet arguments (comme un tableau) en tant que liste d'arguments, alors que call nécessite que vous transmettiez chaque argument séparément.

Ces deux méthodes vous donnent la liberté d'obtenir autant de fantaisie que vous le souhaitez, comme l'implémentation d'une mauvaise version du bind natif d'ECMAScript pour créer une fonction qui sera toujours appelée comme méthode d'un objet à partir d'une fonction d'origine.

function bind (func, obj) { 
    return function () {
        return func.apply(obj, Array.prototype.slice.call(arguments, 1));
    }
}

var obj = { name: "Foo" };

function print() {
    console.log(this.name);
}

printObj = bind(print, obj);

printObj();

Cela se connectera

"Foo"


La fonction bind a beaucoup à faire

  1. obj sera utilisé comme valeur de this
  2. transmettre les arguments à la fonction
  3. puis retourne la valeur

Invocation liée

La méthode bind de chaque fonction vous permet de créer une nouvelle version de cette fonction avec le contexte strictement lié à un objet spécifique. Il est particulièrement utile de forcer l'appel d'une fonction en tant que méthode d'un objet.

var obj = { foo: 'bar' };

function foo() {
    return this.foo;
}

fooObj = foo.bind(obj);

fooObj();

Cela va se connecter:

bar



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow