Question

Je viens de découvrir une situation intéressante en JavaScript. J'ai une classe avec une méthode qui définit plusieurs objets en utilisant la notation objet-littéral. À l'intérieur de ces objets, le pointeur this est utilisé. J'ai déduit du comportement du programme que le pointeur this fait référence à la classe sur laquelle la méthode a été appelée, et non à l'objet créé par le littéral.

Cela semble arbitraire, bien que ce soit la façon dont je m'attendrais à ce que cela fonctionne. Est-ce que ce comportement est défini? Est-ce sécurisé entre navigateurs? Y a-t-il un raisonnement sous-jacent à la raison pour laquelle il est au-delà de "la spécification le dit" (par exemple, est-ce une conséquence d'une décision / philosophie de conception plus large)? Exemple de code réduit:

// inside class definition, itself an object literal, we have this function:
onRender: function() {

    this.menuItems = this.menuItems.concat([
        {
            text: 'Group by Module',
            rptletdiv: this
        },
        {
            text: 'Group by Status',
            rptletdiv: this
        }]);
    // etc
}
Était-ce utile?

La solution

Cannibalisé depuis un autre de mes posts, voici plus que vous n’avez jamais voulu savoir sur this .

Avant de commencer, voici la chose la plus importante à garder à l'esprit à propos de Javascript et à vous répéter quand cela ne vous dit rien. Javascript n'a pas de classes (la classe de ES6 est sucre syntaxique ). Si quelque chose ressemble à une classe, c'est une astuce intelligente. Javascript contient des objets et des fonctions . (Ce n'est pas précis à 100%, les fonctions ne sont que des objets, mais il peut parfois être utile de les considérer comme des choses séparées.)

La variable this est associée à des fonctions. Chaque fois que vous appelez une fonction, this se voit attribuer une certaine valeur, en fonction de la manière dont vous appelez la fonction. Cela s'appelle souvent le modèle d'appel.

Il existe quatre façons d’appeler des fonctions en javascript. Vous pouvez appeler la fonction en tant que méthode , en tant que fonction , en tant que constructeur et avec apply .

En tant que méthode

Une méthode est une fonction attachée à un objet

var foo = {};
foo.someMethod = function(){
    alert(this);
}

Lorsqu'il est appelé en tant que méthode, this sera lié à l'objet dont la fonction / la méthode fait partie. Dans cet exemple, cela sera lié à foo.

En tant que fonction

Si vous avez une fonction autonome, la variable this sera liée à la variable " globale " objet, presque toujours l'objet window dans le contexte d'un navigateur.

 var foo = function(){
    alert(this);
 }
 foo();

C'est peut-être ce qui vous fait craquer , mais ne vous en faites pas. Beaucoup de gens considèrent cela comme une mauvaise décision de conception. Puisqu’un rappel est appelé en tant que fonction et non en tant que méthode, c’est pourquoi vous voyez un comportement qui semble être incohérent.

Beaucoup de gens contournent le problème en faisant quelque chose comme, euh, ceci

var foo = {};
foo.someMethod = function (){
    var that=this;
    function bar(){
        alert(that);
    }
}

Vous définissez une variable that qui pointe sur this . Closure (un sujet qui lui est propre) conserve cela , donc si vous appelez barre comme rappel, il aura toujours une référence.

REMARQUE: En , utilisez le mode strict s'il est utilisé comme fonction, this n'est pas lié à global. (C'est undefined ).

En tant que constructeur

Vous pouvez également appeler une fonction en tant que constructeur. Selon la convention de dénomination que vous utilisez (TestObject), cela peut également être ce que vous êtes en train de faire et c'est ce qui vous a déclenché .

Vous appelez une fonction en tant que constructeur avec le nouveau mot clé.

function Foo(){
    this.confusing = 'hell yeah';
}
var myObject = new Foo();

Lorsqu'il est appelé en tant que constructeur, un nouvel objet est créé et this est lié à cet objet. Encore une fois, si vous avez des fonctions internes et qu'elles sont utilisées en tant que rappels, vous les invoquerez en tant que fonctions et this sera lié à l'objet global. Utilisez cette variable qui = this trick / pattern.

Certaines personnes pensent que le mot clé constructeur / nouveau a été lancé aux programmeurs Java / POO traditionnels pour créer quelque chose de similaire aux classes.

Avec la méthode Apply

Enfin, chaque fonction a une méthode (oui, les fonctions sont des objets en Javascript) appelée "apply". Appliquer vous permet de déterminer la valeur de this et vous permet également de transmettre un tableau d'arguments. Voici un exemple inutile.

function foo(a,b){
    alert(a);
    alert(b);
    alert(this);
}
var args = ['ah','be'];
foo.apply('omg',args);

Autres conseils

Appels de fonction

Les fonctions ne sont qu'un type d'objet.

Tous les objets fonction ont un appel et apply méthodes qui exécutent l'objet Function auquel elles sont appelées.

Lors de l'appel, le premier argument de ces méthodes spécifie l'objet qui sera référencé par le mot-clé this pendant l'exécution de la fonction - s'il s'agit de null ou undefined , l’objet global window est utilisé pour this .

Ainsi, appeler une fonction ...

whereAmI = "window";

function foo()
{
    return "this is " + this.whereAmI + " with " + arguments.length + " + arguments";
}

... avec des parenthèses - foo () - équivaut à foo.call (undefined) ou foo.apply (undefined) , qui est effectivement identique à foo.call (fenêtre) ou foo.apply (fenêtre) .

>>> foo()
"this is window with 0 arguments"
>>> foo.call()
"this is window with 0 arguments"

Des arguments supplémentaires pour appel sont passés comme arguments à l'appel de fonction, alors qu'un seul argument supplémentaire pour apply peut spécifier les arguments de l'appel de fonction sous forme de tableau. comme objet.

Ainsi, foo (1, 2, 3) est équivalent à foo.call (null, 1, 2, 3) ou foo.apply ( null, [1, 2, 3]) .

>>> foo(1, 2, 3)
"this is window with 3 arguments"
>>> foo.apply(null, [1, 2, 3])
"this is window with 3 arguments"

Si une fonction est une propriété d'un objet ...

var obj =
{
    whereAmI: "obj",
    foo: foo
};

... accéder à une référence à la fonction via l'objet et l'appeler avec des parenthèses - obj.foo () - équivaut à foo.call (obj) ou foo.apply (obj) .

Cependant, les fonctions détenues en tant que propriétés d'objets ne sont pas "liées". à ces objets. Comme vous pouvez le voir dans la définition de obj ci-dessus, les fonctions n'étant qu'un type d'objet, elles peuvent être référencées (et peuvent donc être transmises par référence à un appel de fonction ou renvoyées par référence à partir d'une fonction appel). Lorsqu'une référence à une fonction est transmise, aucune information supplémentaire sur son origine from n'est transmise avec elle. C'est pourquoi les événements suivants se produisent:

>>> baz = obj.foo;
>>> baz();
"this is window with 0 arguments"

L'appel à notre référence de fonction, baz , ne fournit aucun contexte pour l'appel. Il est donc identique à baz.call (undefined) , donc this finit par référencer la fenêtre . Si nous voulons que baz sache qu'il appartient à obj , nous devons fournir cette information d'une manière ou d'une autre lorsque baz est appelé, le premier les arguments call ou s'appliquent et les fermetures entrent en jeu.

Chaînes de périmètre

function bind(func, context)
{
    return function()
    {
        func.apply(context, arguments);
    };
}

Lorsqu'une fonction est exécutée, elle crée une nouvelle portée et référence toute portée englobante. Lorsque la fonction anonyme est créée dans l'exemple ci-dessus, elle fait référence à la portée dans laquelle elle a été créée, qui est la portée de bind . Cela s'appelle une "fermeture".

[global scope (window)] - whereAmI, foo, obj, baz
    |
    [bind scope] - func, context
        |
        [anonymous scope]

Lorsque vous essayez d'accéder à une variable, cette "chaîne d'étendue" " est parcouru pour trouver une variable portant le nom donné - si l'étendue actuelle ne contient pas la variable, vous examinez l'étendue suivante de la chaîne, et ainsi de suite jusqu'à atteindre l'étendue globale. Lorsque la fonction anonyme est renvoyée et que bind est terminé, la fonction anonyme a toujours une référence à la portée de bind , donc la portée de bind ne "s'en va" pas.

Etant donné tout ce qui précède, vous devriez maintenant être en mesure de comprendre le fonctionnement de scope dans l'exemple suivant et pourquoi la technique permettant de faire passer une fonction autour de " pre-bound " avec une valeur particulière de this , il aura quand il s'appelle fonctionne:

>>> baz = bind(obj.foo, obj);
>>> baz(1, 2);
"this is obj with 2 arguments"
  

Ce comportement est-il défini? Est-ce   coffre-fort pour tout navigateur?

Oui. Et oui.

  

Y at-il un raisonnement sous-jacent à la raison   c'est comme ça ...

La signification de this est assez simple à déduire:

  1. Si ce est utilisé dans une fonction constructeur et que cette fonction a été appelée avec le mot clé new , this fait référence à l'objet qui Être créé. this continuera à signifier l'objet même dans les méthodes publiques.
  2. Si , ce est utilisé ailleurs, y compris les fonctions protégées imbriquées, il fait référence à la portée globale (qui, dans le cas du navigateur, est l'objet window).

Le deuxième cas est évidemment un défaut de conception, mais il est assez facile de le contourner en utilisant des fermetures.

Dans ce cas, le interne est lié à l'objet global au lieu de la variable this de la fonction externe. C'est la façon dont le langage est conçu.

Voir " JavaScript: les bonnes pièces " Douglas Crockford pour une bonne explication.

J'ai trouvé un bon tutoriel sur ECMAScript ce

  

A cette valeur est un objet spécial lié à l'exécution   le contexte. Par conséquent, il peut être nommé en tant qu’objet de contexte (c.-à-d. Un   objet dans lequel le contexte d'exécution est activé).

Tout objet peut être utilisé comme valeur du contexte.

  

a cette valeur est une propriété du contexte d'exécution, mais pas une   propriété de l'objet variable.

Cette fonctionnalité est très importante car, contrairement aux variables, cette valeur ne participe jamais au processus de résolution des identifiants. C'est à dire. lors de l'accès à cela dans un code, sa valeur est prise directement à partir du contexte d'exécution et sans aucune recherche de chaîne d'étendue. La valeur de this est déterminée une seule fois lors de la saisie du contexte.

Dans le contexte global, a cette valeur est l’objet global lui-même (c’est-à-dire que cette valeur correspond ici à un objet variable)

Dans le cas d'un contexte de fonction, cette valeur peut être différente dans chaque appel de fonction

Référence Javascript-le-noyau et Chapitre-3-ceci

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top