Home >Web Front-end >JS Tutorial >The amazing this_javascript trick in Javascript
This in Javascript is a completely different mechanism from other languages, which may confuse some engineers who write other languages.
1. Mistakenly thinking that this points to the function itself
According to the English grammar of this, it is easy to understand this appearing in a function as the function itself. In JavaScript, functions are first-class citizens and can indeed store attribute values when called. But if you use it incorrectly, it may lead to inconsistencies with actual expectations. For specific circumstances, please see the code below
function fn(num){ this.count++; } fn.count = 0; for(var i=0;i<3;i++){ fn(i); } console.log(fn.count); // 0
If this in the fn function points to its own function, then the attribute value of the count attribute should change, but in fact it remains unchanged. For this problem, some people will use scope to solve it, for example, write like this
var data = { count:0 }; function fn(num){ data.count++; } for(var i=0;i<3;i++){ fn(i); } console.log(data.count); //3
Or more directly like this
function fn(num){ fn.count++; } fn.count = 0; for(var i=0;i<3;i++){ fn(i); } console.log(fn.count);//3
Although both methods output the correct results, they avoid the problem of where this is bound. If the working principle of a thing is not clear, it will often lead to headaches and pain, which will lead to ugly code and poor maintainability.
2. This magical binding rule
2.1 Default binding rules
The first one is the most common this binding, take a look at the code below
function fn(){ console.log(window === this); //浏览器环境 } fn(); //true
Function fn is called directly in the global scope without any other modifications. In this case, the default binding of this is used when the function is called, pointing to the global object.
This makes it clear that this in the first example points to the global variable in the fn function, so this.count++ is equivalent to window.count++ (in the browser environment). Of course, it will not affect the count of the fn function. properties have an impact.
One thing to note is that the above situation can only occur in non-strict mode (strict mode). In strict mode, this will be bound to undefined by default. To avoid contamination of global variables.
2.2 Implicit binding rules
If the function is called with an object as the context, the binding of this will change. this will be bound to the object calling this function, see the following code:
var obj = { a:1, fn:function(){ console.log(this.a); } } obj.fn(); //1
Even if the function declaration is not in the object, the this pointer will still change
function fn(){ console.log(this.a); } var obj = { a:1, fn:fn } obj.fn(); //1
It can be seen that the binding of this is not related to the location of the function definition, but to the caller and the calling method.
Under the implicit binding rules, there are some special things that need to be paid attention to.
2.2.1 Multi-layer object calls this pointer
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
Under multi-level object references, this points to the object of the called function.
2.2.2 Implicit assignment may be lost
View the code below
function fn(){ console.log(this); } var obj = { fn:fn } var fun = obj.fn; fun(); //window
Although fn refers to obj.fun, the method of calling the function is still without any modification, so this is still bound to window.
There is another situation that is easy for everyone to overlook, that is, when passing parameters, implicit assignment will actually be performed.
function fn(){ console.log(this); } function doFn(fn){ fn(); } var obj = { fn:fn } doFn(obj.fn); //window
Implicit binding of this is not a very recommended method, because it is very likely to be lost. If there are requirements for the binding of this in the business, it is recommended to use explicit binding.
2.3 Explicit binding rules
Explicit binding uses the apply and call methods on the function prototype to bind this. The usage is to pass the object you want to bind as the first parameter.
function fn(){ console.log(this); } var obj = {}; fn.call(obj); //{}
Sometimes you want to bind the this of a function to an object, but do not need to call it immediately. In this case, it cannot be done directly using call or apply.
function fn(){ console.log(this); } function bind(fn){ fn(); } var obj = { fn:fn } bind.call(obj,fn); //window
The above example seems to work, but in fact, this of the bind function is bound to the object obj, but fn is still called without any modification, so fn is still the default binding method.
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的相关介绍,希望对大家的学习有所帮助。