En Javascript, il s'agit d'un mécanisme complètement différent des autres langages, ce qui peut dérouter certains ingénieurs qui écrivent d'autres langages.
1. Penser à tort que cela pointe vers la fonction elle-même
Selon la grammaire anglaise de ceci, il est facile de comprendre que cela apparaît dans une fonction comme la fonction elle-même. En JavaScript, les fonctions sont des citoyens de premier ordre et peuvent en effet stocker des valeurs d'attribut lorsqu'elles sont appelées. Mais si vous ne l’utilisez pas correctement, cela peut entraîner des incohérences avec les attentes réelles. Pour des circonstances spécifiques, veuillez consulter le code ci-dessous
function fn(num){ this.count++; } fn.count = 0; for(var i=0;i<3;i++){ fn(i); } console.log(fn.count); // 0
Si ceci dans la fonction fn pointe vers sa propre fonction, alors la valeur de l'attribut count devrait changer, mais en fait elle reste inchangée. Pour ce problème, certaines personnes utiliseront scope pour le résoudre, comme par exemple écrire
var data = { count:0 }; function fn(num){ data.count++; } for(var i=0;i<3;i++){ fn(i); } console.log(data.count); //3
Ou plus directement,
function fn(num){ fn.count++; } fn.count = 0; for(var i=0;i<3;i++){ fn(i); } console.log(fn.count);//3
Bien que les deux méthodes génèrent des résultats corrects, elles évitent le problème de la limite. Si le principe de fonctionnement d'un objet n'est pas clair, cela entraînera souvent des maux de tête et des douleurs, ce qui entraînera un code laid et une mauvaise maintenabilité.
2. Cette règle magique contraignante
2.1 Règles de liaison par défaut
Le premier est le plus courant de cette liaison, jetez un œil au code ci-dessous
function fn(){ console.log(window === this); //浏览器环境 } fn(); //true
La fonction fn est appelée directement dans la portée globale sans aucune autre modification. Dans ce cas, la liaison par défaut de this est utilisée lorsque la fonction est appelée, pointant vers l'objet global.
Cela montre clairement que ceci dans le premier exemple pointe vers la variable globale dans la fonction fn, donc this.count est équivalent à window.count (dans l'environnement du navigateur), et bien sûr, cela n'affecte pas le nombre de les propriétés fn ont un impact.
Une chose à noter est que la situation ci-dessus ne peut se produire qu'en mode non strict (mode strict). En mode strict, cela sera lié à undefined par défaut. Pour éviter la contamination des variables globales.
2.2 Règles de liaison implicites
Si la fonction est appelée avec un objet comme contexte, la liaison de celui-ci changera. cela sera lié à l'objet appelant cette fonction, voir le code suivant :
var obj = { a:1, fn:function(){ console.log(this.a); } } obj.fn(); //1
Même si la déclaration de fonction n'est pas dans l'objet, le pointeur this changera quand même
function fn(){ console.log(this.a); } var obj = { a:1, fn:fn } obj.fn(); //1
On peut voir que la liaison de ceci n'est pas liée à l'emplacement de la définition de la fonction, mais à l'appelant et à la méthode appelante.
En vertu des règles implicites contraignantes, il y a certaines choses particulières auxquelles il faut prêter attention.
2.2.1 L'objet multicouche appelle ce pointeur
function fn(){ console.log(this.a); } var obj = { a:1, obj2:obj2 } var obj2 = { a:2, obj3:obj3 } var obj3 = { a:3, fn:fn } obj.obj2.obj3.fn(); //3
Sous les références d'objets multi-niveaux, cela pointe vers l'objet de la fonction appelée.
2.2.2 L'affectation implicite peut être perdue
Voir le code ci-dessous
function fn(){ console.log(this); } var obj = { fn:fn } var fun = obj.fn; fun(); //window
Bien que fn fasse référence à obj.fun, la méthode d'appel de la fonction est toujours sans aucune modification, elle est donc toujours liée à window.
Il existe une autre situation facile à ignorer pour tout le monde, à savoir que lors du passage des paramètres, une affectation implicite sera en fait effectuée.
function fn(){ console.log(this); } function doFn(fn){ fn(); } var obj = { fn:fn } doFn(obj.fn); //window
La liaison implicite de ceci n'est pas une méthode très recommandée, car elle risque très probablement d'être perdue. S'il existe des exigences pour la liaison de ceci dans l'entreprise, il est recommandé d'utiliser une liaison explicite.
2.3 Règles contraignantes explicites
La liaison explicite utilise les méthodes apply et call sur le prototype de fonction pour lier cela. L'utilisation consiste à transmettre l'objet que vous souhaitez lier comme premier paramètre.
function fn(){ console.log(this); } var obj = {}; fn.call(obj); //{}
Parfois, vous souhaitez lier le this d'une fonction à un objet, mais vous n'avez pas besoin de l'appeler immédiatement. Dans ce cas, cela ne peut pas être fait directement en utilisant call ou apply.
function fn(){ console.log(this); } function bind(fn){ fn(); } var obj = { fn:fn } bind.call(obj,fn); //window
L'exemple ci-dessus semble fonctionner, mais en fait, celle de la fonction bind est liée à l'objet obj, mais fn est toujours appelé sans aucune modification, donc fn est toujours la méthode de liaison par défaut.
function fn(){ console.log(this); } function bind(fn,obj){ return function(){ fn.apply(obj,arguments); } } var obj = { fn:fn } var fun = bind(fn,obj); fun(); //obj
这样调用,就可以将灵活多变的 this ,牢牢的控制住了,因为 fn 的调用方式为 apply 调用。所以,this 就被绑定在传入的 obj 对象上,在 ES5 当中,函数的原型方法上多了一个 bind。效果与上面的函数基本一致,具体用法限于篇幅就不多说了。
2.4 new 绑定
new 是一个被很多人误解的一个关键字,但实际上 javascript 的 new 与传统面向对象的语言完全不同。
个人把 new 理解为一种特殊的函数调用,当使用 new 关键字来调用函数的时候,会执行下面操作,
function fn(a){ this.a = a; } fn.prototype.hi = function(){ console.log('hi') } var obj = new fn(2); console.log(obj); function fn(a){ this.a = a; return {}; } var obj = new fn(2); console.log(obj); //{}
2.5 特殊的传参
null 和 undefined 也是可以作为 this 的绑定对象的,但是实际上应用的是默认的绑定。
但是这种传参的实际效用是什么呢?
常见的用法是将一个数组展开,作为参数传入参数。比如
function fn(a,b){ console.log('a:',a,'b:',b); } fn.apply(null,[1,2]); // a: 1 b: 2
但是这种用法会有一个坑,那就是如果函数存在了 this ,那么就会应用默认的绑定规则,将 this 绑定在全局对象上,发生于预期不一致的情况。为了代码更加稳健,可以使创建一个比空对象更空的对象。
var obj = Object.create(null); console.log(obj.__proto__); //undefined var obj2 = {} console.log(obj2.__proto__); //Object {}
Object原型上有一个 create 方法,这个方法会创建一个对象,然后将对象的原型指向传入的参数,所以传入 null 的话,产生一个没有 prototype 的对象,所以会比空对象更加"空"。
所以传入这个对象,会比传入 null 更加安全。
var obj = Object.create(null); fn.apply(obj,[1,2]);
2.6 根据作用域来决定 this 的绑定
在 ES6 当中,出现了一个新的函数类型,箭头函数。
如果使用箭头函数,那么就不会使用上面提到的四种 this 绑定方式,而是根据作用域来决定
比较常见的是用于事件函数和定时器的情况。
下面是比较常见的传统 this 写法
function fn(){ var _this = this; setTimeout(function(){ console.log(_this.a); },100) } var obj = { a:2 } fn.call(obj); //2
如果使用箭头函数则可以这么写
function fn(){ setTimeout(()=>{ //this 来源于 fn 函数的作用域 console.log(this.a); },100) } var obj = { a:2 } fn.call(obj); //2
2.7 事件函数当中 this 的绑定机制
如果是在事件函数当中,this 的绑定是指向触发事件的 DOM 元素的,
$('body')[0].addEventListener('click',function(){ console.log(this); },false);
点击 body 元素之后,控制台则会显示 body 元素
3. 小结
如果想判断一个函数的 this 绑定在哪里,首先是找到函数的调用位置,之后是按照规则来判断。
以上就是关于Javascript中神奇的this的相关介绍,希望对大家的学习有所帮助。