Maison  >  Article  >  interface Web  >  Analyse approfondie de l'utilisation de ce mot-clé dans la programmation JavaScript_Connaissances de base

Analyse approfondie de l'utilisation de ce mot-clé dans la programmation JavaScript_Connaissances de base

WBOY
WBOYoriginal
2016-05-16 15:33:031910parcourir

Que signifie exactement cela en JavaScript ? Beaucoup de gens vous diront que cela fait référence à l’objet actuel. Est-ce exact ? C'est vrai dans la plupart des cas. Par exemple, nous écrivons souvent du JavaScript comme ceci sur les pages Web :

<input type="submit" value="提交" onclick="this.value='正在提交数据'" />

Ceci ici fait évidemment référence à l'objet actuel, qui est le bouton de soumission. Habituellement, la situation lorsque nous utilisons ceci est similaire à celle-ci. Mais existe-t-il des situations où ce n’est pas le cas ?

Regardez cet exemple :

var foo = function() {
  console.log(this);
}
foo();
new foo();

Comparez les résultats d'exécution de foo() et new foo(). Vous constaterez que dans le premier, cela ne pointe pas vers foo lui-même, mais vers l'objet fenêtre de la page actuelle, tandis que le second pointe réellement vers foo. . Pourquoi est-ce ?

En fait, cela implique une fonctionnalité importante de JavaScript, qui est ce qu'on appelle la « fermeture ». Le concept de clôture n’est pas complexe, mais il n’est pas si simple qu’il puisse être expliqué clairement en une ou deux phrases. J'aborderai cette fonctionnalité la plus importante de Javascript dans un prochain article. Maintenant, ce que je veux vous dire, c'est que la portée en JavaScript devient très importante à cause des fermetures.

En termes simples, ce qu'on appelle la portée fait référence à l'environnement dans lequel une fonction est créée. La valeur de cette variable, si elle n'est pas spécifiée, correspond à la portée actuelle de la fonction.

Dans l'exemple précédent, la fonction foo() est dans la portée globale (voici l'objet window), donc la valeur de ceci est l'objet window actuel. Sous la forme de new foo(), une copie de foo() est effectivement créée et des opérations sont effectuées sur cette copie, voici donc la copie de foo().

Cela peut paraître un peu abstrait, regardons un exemple pratique :

<input type="button" id="aButton" value="demo" onclick="" />
<script type="text/javascript">
function demo() {
  this.value = Math.random();
}
</script>

Si vous appelez directement la fonction demo(), le programme signalera une erreur car la fonction demo est définie dans l'objet window, donc le propriétaire (portée) de demo est window, et celui-ci de demo est également window. La fenêtre n'a pas d'attribut value, une erreur a donc été signalée.

2015119174624685.png (391×372)

Si on ajoute une copie de cette fonction à un élément HTML en créant une copie, alors son propriétaire devient cet élément, et cela fait également référence à cet élément :

document.getElementById("aButton").onclick = demo;

Cela définit l'attribut onlick de aButton sur une copie de demo(), et cela pointe également vers aButton.

2015119174656357.png (391×373)

Vous pouvez même créer différentes copies de la fonction pour plusieurs éléments HTML différents. Le propriétaire de chaque copie est l'élément HTML correspondant, et leurs noms respectifs pointent également vers leurs propriétaires, ce qui ne prêtera pas à confusion.

2015119174716805.png (391×482)

Cependant, si vous définissez l'événement onlick d'un élément comme ceci :

<input type="button" id="aButton" value="demo" onclick="demo()" />

Après avoir cliqué sur ce bouton, vous constaterez que le programme signalera à nouveau une erreur - cela pointe à nouveau vers la fenêtre !

En fait, cette méthode ne crée pas de fonction pour le programme, mais fait uniquement référence à la fonction.

Regardons de plus près les différences.

Utilisez la méthode qui crée une copie de la fonction :

<input type="button" id="aButton" value="demo" />
<script type="text/javascript">
var button = document.getElementById("aButton");
function demo() {
  this.value = Math.random();
}
button.onclick= demo;
alert(button.onclick);
</script>

Le résultat résultant est :

function demo() {
  this.value = Math.random();
}

Comment utiliser les références de fonctions :

<input type="button" id="aButton" value="demo" onclick="demo()" />

Le résultat résultant est :

function onclick() {
  demo();
}

Vous pouvez voir la différence de cette façon. Dans la méthode de référence de fonction, l'événement onclick appelle simplement la fonction demo() directement, et la portée de la fonction demo() est toujours l'objet window, donc cela pointe toujours vers la fenêtre.

2015119174740033.png (391×368)

Cela soulève une autre question : puisque les copies de fonctions sont si faciles à utiliser, pourquoi avons-nous besoin de références de fonctions ? La réponse est la performance. Chaque fois qu'une copie d'une fonction est créée, le programme allouera une certaine quantité de mémoire pour la copie de la fonction. Dans les applications réelles, la plupart des fonctions ne sont pas nécessairement appelées, cette partie de la mémoire est donc gaspillée. En utilisant des références de fonction, le programme allouera uniquement de la mémoire à la fonction elle-même, tandis que les références alloueront uniquement des pointeurs, ce qui est beaucoup plus efficace. Programmeurs, économiser c'est le principal, hein

Regardons donc une meilleure solution :

<script type="text/javascript">
function demo(obj) {
  obj.value = Math.random();
}
</script>
<input type="button" value="demo" onclick="demo(this)" />
<input type="button" value="demo" onclick="demo(this)" />
<input type="button" value="demo" onclick="demo(this)" />

 

这样,效率和需求就都能兼顾了。

 
this的指向
JavaScript由于其在运行期进行绑定的特性,JavaScript 中的 this 可以是全局对象、当前对象或者任意对象,这完全取决于函数的调用方式。JavaScript 中函数的调用有以下几种方式:作为对象方法调用,作为函数调用,作为构造函数调用,和使用 apply 或 call 调用。常言道,字不如表,表不如图。为了让人更好的理解JavaScript this 到底指向什么?下面用一张图来进行解释:

2015119174758093.jpg (1251×421)

上图我称之为”JavaScript this决策树“(非严格模式下)。下面通过例子来说明这个图如何来帮助我们对this进行判断:

var point = { 
 x : 0, 
 y : 0, 
 moveTo : function(x, y) { 
   this.x = this.x + x; 
   this.y = this.y + y; 
   } 
 };
//决策树解释:point.moveTo(1,1)函数不是new进行调用,进入否决策,
//是用dot(.)进行调用,则指向.moveTo之前的调用对象,即point
point.moveTo(1,1); //this 绑定到当前对象,即point对象

point.moveTo()函数在 “JavaScript this决策树“中进行判定的过程是这样的:

1)point.moveTo函数调用是用new进行调用的么?这个明显不是,进入“否”分支,即函数是否用dot(.)进行调用?;

2)point.moveTo函数是用dot(.)进行调用的,即进入“是”分支,即这里的this指向point.moveTo中.之前的对象point;

图解point.moveTo函数的this指向什么的解析图如下图所示:

2015119175054859.jpg (1245×416)

再举例,看下面的代码:

function func(x) { 
 this.x = x; 
 } 
func(5); //this是全局对象window,x为全局变量
//决策树解析:func()函数是用new进行调用的么?为否,进入func()函数是用dot进行调用的么?为否,则 this指向全局对象window
x;//x => 5

func()函数在 “JavaScript this决策树“中进行判定的过程是这样的:

1)func(5)函数调用是用new进行调用的么?这个明显不是,进入“否”分支,即函数是否用dot(.)进行调用?;

2)func(5)函数不是用dot(.)进行调用的,即进入“否”分支,即这里的this指向全局变量window,那么this.x实际上就是window.x;

图解func函数的this指向什么的解析图如下图所示:

2015119175222664.jpg (1242×416)

针对作为函数直接调用的方式,下面看一个复杂的例子:

var point = { 
 x : 0, 
 y : 0, 
 moveTo : function(x, y) { 
   // 内部函数
   var moveX = function(x) { 
   this.x = x;//this 指向什么?window
  }; 
  // 内部函数
  var moveY = function(y) { 
  this.y = y;//this 指向什么?window
  }; 
  moveX(x); 
  moveY(y); 
  } 
 }; 
 point.moveTo(1,1); 
 point.x; //=>0 
 point.y; //=>0 
 x; //=>1 
 y; //=>1

point.moveTo(1,1)函数实际内部调用的是moveX()和moveY()函数, moveX()函数内部的this在 “JavaScript this决策树“中进行判定的过程是这样的:

1)moveX(1)函数调用是用new进行调用的么?这个明显不是,进入“否”分支,即函数是否用dot(.)进行调用?;

2)moveX(1)函数不是用dot(.)进行调用的,即进入“否”分支,即这里的this指向全局变量window,那么this.x实际上就是window.x;

下面看一下作为构造函数调用的例子:

function Point(x,y){ 
  this.x = x; // this &#63;
  this.y = y; // this &#63;
 }
var np=new Point(1,1);
np.x;//1
var p=Point(2,2);
p.x;//error, p是一个空对象undefined
window.x;//2

Point(1,1)函数在var np=new Point(1,1)中的this在 “JavaScript this决策树“中进行判定的过程是这样的:

1)var np=new Point(1,1)调用是用new进行调用的么?这个明显是,进入“是”分支,即this指向np;

2)那么this.x=1,即np.x=1;

Point(2,2)函数在var p= Point(2,2)中的this在 “JavaScript this决策树“中进行判定的过程是这样的:

1)var p= Point(2,2)调用是用new进行调用的么?这个明显不是,进入“否”分支,即函数是否用dot(.)进行调用?;

2)Point(2,2)函数不是用dot(.)进行调用的?判定为否,即进入“否”分支,即这里的this指向全局变量window,那么this.x实际上就是window.x;

3)this.x=2即window.x=2.

最后看一下函数用call 和apply进行调用的例子:

function Point(x, y){ 
  this.x = x; 
  this.y = y; 
  this.moveTo = function(x, y){ 
    this.x = x; 
    this.y = y; 
  } 
 } 

var p1 = new Point(0, 0); 
var p2 = {x: 0, y: 0}; 
p1.moveTo.apply(p2, [10, 10]);//apply实际上为p2.moveTo(10,10)
p2.x//10

Le processus de la fonction p1.moveTo.apply(p2,[10,10]) dans "JavaScript cet arbre de décision" est le suivant :

Nous savons que les deux méthodes apply et call sont extrêmement puissantes. Elles permettent de changer le contexte d'exécution de la fonction, c'est-à-dire l'objet qui y est lié. p1.moveTo.apply(p2,[10,10]) est en fait p2.moveTo(10,10). Alors p2.moveTo(10,10) peut être interprété comme :

1) La fonction p2.moveTo(10,10) est-elle appelée en utilisant new ? Ce n'est évidemment pas le cas. Allez dans la branche "Non", c'est-à-dire que la fonction est appelée avec un point(.) ? ;

2) La fonction p2.moveTo(10,10) est appelée avec point(.), c'est-à-dire qu'elle entre dans la branche "oui", c'est-à-dire qu'elle pointe ici vers l'objet précédent p2 dans p2.moveTo( 10,10). , donc p2.x=10;

Concernant le processus de l'environnement d'exécution des fonctions JavaScript, il existe une très bonne description dans la bibliothèque de documents IBM Developerworks. L'extrait est le suivant :

« Une fonction en JavaScript peut être exécutée comme une fonction ordinaire ou comme une méthode d'un objet. C'est la principale raison pour laquelle cela a une signification si riche. Lorsqu'une fonction est exécutée, un environnement d'exécution (ExecutionContext) est créé. Le comportement de la fonction se produit dans cet environnement d'exécution.Lors de la construction de l'environnement d'exécution, JavaScript créera d'abord la variable arguments, qui contient les paramètres transmis lors de l'appel de la fonction, puis créera la chaîne de portée et l'initialisera d'abord. liste des paramètres de la fonction, la valeur est la valeur correspondante dans la variable arguments. S'il n'y a pas de valeur correspondante dans la variable arguments, le paramètre formel est initialisé à non défini. Si la fonction contient des fonctions internes, ces fonctions internes sont initialisées si. non, continuez à initialiser. Pour les variables locales définies dans cette fonction, il convient de noter que ces variables sont initialisées à undefined à ce moment-là, et leurs opérations d'affectation ne seront exécutées que lorsque la fonction sera exécutée après l'environnement d'exécution (ExecutionContext). est créé avec succès. C'est très important pour nous de comprendre le rôle des variables en JavaScript. Compte tenu de l'espace, nous n'aborderons pas ce sujet ici. Enfin, attribuez une valeur à cette variable. ci-dessus, il sera attribué à cet objet global, objet actuel, etc. selon la méthode d'appel de fonction (jusqu'à présent) est créée avec succès, la fonction commence à s'exécuter ligne par ligne et les variables requises sont lues. à partir de l'environnement d'exécution précédemment construit (ExecutionContext) »
. Comprendre ce paragraphe contribuera grandement à comprendre les fonctions Javascript.

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn