Pour comprendre cela en fonction de l'endroit où cela se trouve, la situation peut être à peu près divisée en trois types:
1. Dans la fonction: il s'agit généralement d'un paramètre implicite.
2. En dehors de la fonction (dans la portée supérieure): dans le navigateur, cela fait référence à l'objet global; Dans Node.js, fait référence aux exportations de modules.
3. La chaîne passée à EVAL (): si EVAL () est appelé directement, cela fait référence à l'objet actuel; Si EVAL () est appelé indirectement, cela fait référence à l'objet global.
Nous avons effectué des tests correspondants pour ces catégories:
1. Ceci dans la fonction
Les fonctions peuvent essentiellement représenter toutes les structures invoquées dans JS, c'est donc également le scénario le plus courant où cela est utilisé, et les fonctions peuvent être subdivisées dans les trois rôles suivants:
Fonctions réelles
Constructeur
méthode
1.1 Ceci en fonctions réelles
Dans les fonctions réelles, la valeur de ceci est un modèle qui dépend du contexte dans lequel il est situé.
Mode bâclé: il s'agit d'un objet global (fenêtre dans le navigateur).
La copie de code est la suivante:
fonction SLOLPYFUNC () {
console.log (cette fenêtre ===); // vrai
}
SLASTPYFUNC ();
Mode strict: la valeur de ceci n'est pas définie.
La copie de code est la suivante:
fonction strictfunc () {
«utiliser strict»;
console.log (this === Undefined); // vrai
}
strictfunc ();
Il s'agit d'un paramètre implicite d'une fonction, donc sa valeur est toujours la même. Cependant, vous pouvez définir cette valeur en utilisant des méthodes Call () ou Appliquer () pour afficher la valeur.
La copie de code est la suivante:
fonction func (arg1, arg2) {
console.log (ceci); // 1
Console.log (Arg1); // 2
Console.log (Arg2); // 3
}
Func.Call (1, 2, 3); // (ceci, arg1, arg2)
func.Apply (1, [2, 3]); // (ceci, arraywithargs)
1.2 Ceci dans le constructeur
Vous pouvez utiliser une fonction en tant que constructeur via Nouveau. La nouvelle opération crée un nouvel objet et passe cet objet dans le constructeur à travers cela.
La copie de code est la suivante:
var a enregistré cela;
Fonction Contr () {
SavetThis = this;
}
var Inst = new CONTR ();
console.log (SavedThis === Inst); // vrai
Le principe de mise en œuvre de la nouvelle opération dans JS est à peu près affiché dans le code suivant (voir ici pour une implémentation plus précise, cette implémentation est également plus compliquée):
La copie de code est la suivante:
fonction newoperator (help, arraywithargs) {
var thisValue = object.Create (CONSTR.prototype);
CONSTR.Apply (thisValue, ArrayWithArgs);
retourner cette valeur;
}
1.3 Ceci dans la méthode
Dans les méthodes, l'utilisation de cela a tendance à être plus dans le langage traditionnel orienté objet: le récepteur pointé par cela, c'est-à-dire l'objet contenant cette méthode.
La copie de code est la suivante:
var obj = {
Méthode: fonction () {
console.log (this === obj); // vrai
}
}
obj.Method ();
2. Cette portée
Dans le navigateur, la portée est la portée globale, et cela fait référence à cet objet global (comme fenêtre):
La copie de code est la suivante:
<cript>
console.log (cette fenêtre ===); // vrai
</cript>
Dans Node.js, vous exécutez généralement des fonctions dans les modules. Par conséquent, la portée de niveau supérieur est une portée de module très spéciale:
La copie de code est la suivante:
// `global` (pas` fenêtre`) se référer à l'objet global:
console.log (math === global.math); // vrai
// «Ceci» ne fait pas référence à l'objet global:
console.log (this! == global); // vrai
// «Cela» fait référence aux exportations d'un module:
console.log (this === module.exports); // vrai
3. Ceci dans EVAL ()
eval () peut être appelé directement (en appelant le nom de la fonction «eval») ou indirectement (par d'autres moyens, tels que call ()). Pour plus de détails, veuillez consulter ici.
La copie de code est la suivante:
// Fonctions réelles
fonction SLOLPYFUNC () {
console.log (eval ('this') === fenêtre); // vrai
}
SLASTPYFUNC ();
fonction strictfunc () {
«utiliser strict»;
console.log (eval ('this') === Undefined); // vrai
}
strictfunc ();
// constructeurs
var a enregistré cela;
Fonction Contr () {
SAVEDTHIS = EVAL ('this');
}
var Inst = new CONTR ();
console.log (SavedThis === Inst); // vrai
// Méthodes
var obj = {
Méthode: fonction () {
console.log (eval ('this') === obj); // vrai
}
}
obj.Method ();
4. Pièges liés à cela
Vous devez faire attention aux 3 pièges liés à cela qui seront introduits ci-dessous. Notez que dans l'exemple suivant, l'utilisation du mode strict peut améliorer la sécurité du code. Étant donné que dans les fonctions réelles, la valeur de cela n'est pas définie, vous obtiendrez un avertissement lorsque quelque chose ne va pas.
4.1 J'ai oublié d'utiliser de nouveaux
Si vous n'utilisez pas de nouveau pour appeler le constructeur, vous utilisez réellement une fonction réelle. Ce ne sera donc pas la valeur que vous attendez. En mode bâclé, cela pointe vers la fenêtre et vous créerez des variables globales:
La copie de code est la suivante:
Point de fonction (x, y) {
this.x = x;
this.y = y;
}
var p = point (7, 5); // Nous oublions nouveau!
console.log (p === Undefined); // vrai
// des variables globales ont été créées:
console.log (x); // 7
console.log (y); // 5
Cependant, si vous utilisez un mode strict, vous obtiendrez toujours un avertissement (ce === Undefined):
La copie de code est la suivante:
Point de fonction (x, y) {
«utiliser strict»;
this.x = x;
this.y = y;
}
var p = point (7, 5);
// TypeError: Impossible de définir la propriété «x» non définie
4.2 Méthode d'utilisation inappropriée
Si vous obtenez directement la valeur d'une méthode (sans l'appeler), vous utilisez cette méthode en fonction. Lorsque vous souhaitez passer une méthode en tant que paramètre dans une fonction ou une méthode d'appel, vous le ferez probablement. C'est le cas avec SetTimeout () et les gestionnaires d'événements d'enregistrement. J'utiliserai la méthode callit () pour simuler ce scénario:
La copie de code est la suivante:
/ ** similaire à setTimeout () et SetImMediate () * /
fonction callit (func) {
func ();
}
Si vous appelez une méthode en fonction en mode SLAPPY, * cela * pointe vers l'objet global, donc le créé sera par la suite des variables globales.
La copie de code est la suivante:
var compter = {
Compter: 0,
// Méthode en mode SLAPPY
Inc: function () {
this.Count ++;
}
}
callit (compter.inc);
// n'a pas fonctionné:
Console.log (Counter.Count); // 0
// Au lieu de cela, une variable globale a été créée
// (NAN est le résultat de l'application ++ à un non-défini):
console.log (count); // nan
Si vous faites cela en mode strict, cela n'est pas défini et vous n'obtiendrez toujours pas le résultat souhaité, mais au moins vous obtiendrez un avertissement:
La copie de code est la suivante:
var compter = {
Compter: 0,
// Méthode en mode strict
Inc: function () {
«utiliser strict»;
this.Count ++;
}
}
callit (compter.inc);
// TypeError: Impossible de lire la propriété «Nombre» de non définie
Console.log (Counter.Count);
Pour obtenir les résultats attendus, vous pouvez utiliser bind ():
La copie de code est la suivante:
var compter = {
Compter: 0,
Inc: function () {
this.Count ++;
}
}
callit (counter.inc.bind (compteur));
// Cela a fonctionné!
Console.log (Counter.Count); // 1
bind () crée une autre fonction qui peut toujours définir cette valeur sur contrer.
4.3 Cachez ceci
Lorsque vous utilisez des fonctions dans les méthodes, vous ignorez souvent que les fonctions ont le leur. Ceci est différent de la méthode, vous ne pouvez donc pas mélanger ces deux ensemble. Pour plus de détails, veuillez consulter le code suivant:
La copie de code est la suivante:
var obj = {
Nom: «Jane»,
Amis: ['Tarzan', 'Cheeta'],
LOOP: fonction () {
«utiliser strict»;
this.friend.Forach (
fonction (ami) {
console.log (this.name + 'sait' + ami);
}
));
}
};
obj.loop ();
// TypeError: Impossible de lire la propriété «Nom» de non-défini
Ce nom dans la fonction dans l'exemple ci-dessus ne peut pas être utilisé car la valeur de cette fonction n'est pas définie, ce qui est différent de celui-ci dans la boucle de méthode (). Voici trois idées pour résoudre ce problème:
1.
La copie de code est la suivante:
LOOP: fonction () {
«utiliser strict»;
var that = this;
this.friends.ForEach (fonction (ami) {
console.log (that.name + 'connaissances' + ami);
});
}
2. Bind (). Utilisez Bind () pour créer une fonction. Cette fonction contient toujours la valeur que vous souhaitez passer (dans l'exemple suivant, cette méthode):
La copie de code est la suivante:
LOOP: fonction () {
«utiliser strict»;
this.friends.ForEach (fonction (ami) {
console.log (this.name + 'sait' + ami);
} .bind (this));
}
3. Utilisez le deuxième paramètre de Foreach. Le deuxième paramètre de Foreach sera transmis dans la fonction de rappel et utilisé comme celui de la fonction de rappel.
La copie de code est la suivante:
LOOP: fonction () {
«utiliser strict»;
this.friends.ForEach (fonction (ami) {
console.log (this.name + 'sait' + ami);
}, ce);
}
5. meilleures pratiques
En théorie, je pense que la fonction réelle n'appartient pas à elle-même, et la solution ci-dessus est également basée sur cette idée. Ecmascript 6 utilise la fonction Arrow pour réaliser cet effet, qui est une fonction qui n'a pas la sienne. Dans une telle fonction, vous pouvez l'utiliser à volonté, sans vous soucier de savoir s'il y a une existence implicite.
La copie de code est la suivante:
LOOP: fonction () {
«utiliser strict»;
// Le paramètre de foreach () est une fonction flèche
this.friends.Forach (ami => {
// «ceci» est «this» de Loop
console.log (this.name + 'sait' + ami);
});
}
Je n'aime pas que certaines API considèrent cela comme un paramètre supplémentaire à une fonction réelle:
La copie de code est la suivante:
AVANTEACH (fonction () {
this.addmatchers ({
ToBeinRange: fonction (démarrage, fin) {
...
}
});
});
Écrivant un paramètre implicite tel qu'il est explicitement transmis, le code semble mieux à comprendre, et cela est cohérent avec les exigences de la fonction Arrow:
La copie de code est la suivante:
AVANTEACH (API => {
api.addmatchers ({
tabeinrange (start, fin) {
...
}
});
});