Qu'est-ce que 'ceci' avant qu'un objet soit instancié dans js?

Je ne comprends pas ce qui suit:

var x = function() { this.foo="foo"; return function() { this.bar = "bar"; return foo+bar; }; }(); // returns inner alert(x()); // 'foobar', so both 'this' variables are set alert(x.bar); // undefined - but wasn't it used correctly? alert(new x().bar); // ok, works 

Mon hypothèse était que le "scope" / la variable-map par défaut est généré et utilisé pour la première fois, alors que «new» est appelé, un nouvel objet (function?) Avec un nouveau «this» est envoyé et retourné. Ou, peut-être que X n'est-il pas un objet approprié? Mais alors, comment 'cela' finit-on à être défini et utilisé pour faire 'foobar'?

Que dois-je savoir pour comprendre cela?

Examinons d'abord certains bons points de JavaScript, puis nous pouvons traiter votre exemple.

Contexte de la fonction

Un contexte de malentendu est un contexte. Chaque fonction est appelée dans un contexte, qui est disponible en utilisant un mot this clé. Écrivons une fonction que nous pouvons utiliser pour inspecter les contextes:

 var probe = function(){ // if the context doesn't have a name, let's name it if(!this.name){ this.name = "lumberjack"; } // print the name of my context console.log(this.name); }; 

Et c'est parti:

 name = "global!"; // when we call a function normally it still have a context: // the global context probe(); // prints: global! var ctx = {name: "ctx"}; // we can set a context explicitly using call() probe.call(ctx); // prints: ctx // we can set a context explicitly using apply() probe.apply(ctx); // prints: ctx // it is set implicitly, if we call a function as a member ctx.fun = probe; ctx.fun(); // prints: ctx // or we can create a brand new object and set it as a context: // that's what "new" does var t = new probe(); // prints: lumberjack // let's sum it up: console.log(name); // prints: global! console.log(ctx.name); // prints: ctx console.log(t.name); // prints: lumberjack 

C'est pourquoi il est si facile de gâcher et de tomber par inadvertance dans le contexte global.

Valeur de retour dans le constructeur

Beaucoup de gens sont confus quand ils voient un constructeur renvoyer une valeur. C'est légal. Constructeur peut renvoyer un objet, une fonction ou un tableau. Cette valeur va être utilisée comme une instance. L'ancienne instance va être rejetée.

 var myClass = function(){ // if it is called as a constructor, "this" will be a new instance // let's fill it up: this.a = 42; this.b = "Ford"; this.c = function(){ return "Perfect"; }; // done? let's discard it completely! // and now for something completely different... return { owner: "Monty Python", establishment: "Flying Circus" }; }; var t = new myClass(); alert(t.owner + "'s " + t.establishment); 

Comme prévu, il montre "Monty Python's Flying Circus".

Si un constructeur renvoie autre chose (par exemple, un nombre, une chaîne, le null, le non défini), le résultat renvoyé sera rejeté et l'ancienne instance sera utilisée.

L'exemple

Votre exemple est difficile à comprendre principalement en raison de la façon dont il a été écrit. Simplifiez-le en réécritant.

Tout d'abord traitons le x :

 var x = function() { this.foo = "foo"; return function() { this.bar = "bar"; return foo + bar; }; }(); // returns inner 

Comme nous pouvons voir la fonction anonyme (la 1ère function ) est exécuté immédiatement, nous pouvons donc l'intégrer:

 // next assignment can be simplified because // top "this" is window or the global scope //this.foo = "foo"; => foo = "foo"; x = function() { this.bar = "bar"; // this line depends on its context, or "this" return foo + bar; // this line uses global "foo" and "bar" }; 

Donc, à la fin, nous avons deux variables globales: foo (une chaîne) et x (une fonction).

Passons maintenant la 1ère alerte:

 alert(x()); // 'foobar', so both 'this' variables are set 

Encore une fois, mettons en ligne x() :

 // next assignment can be simplified because // top "this" is window or the global scope //this.bar = "bar"; => bar = "bar"; // at this moment both global "foo" and "bar" are set alert(foo + bar); // => "foo" + "bar" => "foobar" 

La 2 ème alerte est également simple:

 alert(x.bar); // undefined - but wasn't it used correctly? 

Il n'a pas besoin de beaucoup de réécriture. x est une fonction, nous n'avons pas ajouté de propriétés, donc x.bar est indéfini. Si vous l'ajoutez, vous pouvez voir les résultats:

 x.bar = "bar2"; alert(x.bar); // bar2 

La 3ème alerte démontre l'OOP de JavaScript en action:

 alert(new x().bar); // ok, works 

(Une note latérale: cela fonctionne uniquement parce que vous avez couru x() abord, sinon il souffle car la bar n'est pas définie).

Réécrivez-le comme ça:

 var t = new x(); alert(t.bar); // bar 

Maintenant, analysons le constructeur. Il comporte deux déclarations: une affectation et un retour. Ce dernier est ignoré car il renvoie une chaîne. Nous pouvons donc le réécrire comme ça:

 x = function(){ this.bar = "bar"; }; var t = new x(); alert(t.bar); // bar 

J'espère que tout semble facile maintenant.

C'est la principale plainte du nouvel opérateur …

Cet opérateur crée un nouvel objet qui hérite du prototype de la fonction du constructeur d'opérande, puis il appelle la fonction, en attribuant le nouvel objet.

Si vous oubliez d'utiliser le nouvel opérateur lorsque vous appelez une fonction de constructeur, vous obtenez un appel de fonction normal, et this est lié à l'objet global ( window ) au lieu d'un nouvel objet.

Votre fonction va ajouter des variables globales chaque fois qu'elle utilise this tentative d'initialisation de sa propre instance.

Dans votre exemple, l'objet global se retrouve avec deux nouvelles variables:

 window.foo window.bar 

En raison de cela, certaines personnes préfèrent l' héritage prototypique au lieu de l'approche pseudo-classique.

Pour répondre à la question que vous posez dans votre titre: this fera référence à l'objet global.

Gardez à l'esprit que this ne se comporte pas en JavaScript comme dans des langages comme Java ou C ++; Il est utilisé uniquement pour le contexte et ne fera pas nécessairement référence au même type d'objet chaque fois qu'une fonction donnée est appelée. À partir de la documentation MDC :

Il y a quatre manières de réussir
[…]
Si aucune des façons ci-dessus n'est utilisée, l'objet global est transmis comme objet contextuel, par exemple, lorsque cela se produit au niveau supérieur hors de tout constructeur, ou lorsqu'une fonction est appelée sans être appelée comme méthode d'un objet, comme Dans func(arg1, arg2) .

Pour les réponses au reste de votre question, veuillez consulter: Juste quand je pense que je comprends enfin la portée de Javascript …

Comme Shog9 l'a dit, ce n'est pas la même que la portée normale comme vous voyez en Java, etc.

Le Javascript d'AFAIK utilise la portée dynamique, comme le fait le LISP / eLisp commun, et non le champ de mesure lexical comme Scheme / Lisp-1.