MEMEPh. ideas that are worth sharing...

You still don't understand this

I. Introduction


The this keyword is one of the most complex mechanisms in JavaScript. It is a special keyword that is automatically defined in the scope of all functions. This binding has always been a very confusing thing for JavaScript developers who haven't invested the time to learn the this mechanism.
 

2. Understand this


The first step in learning about this is to understand that this refers neither to the function itself nor to the lexical scope of the function. You may have been misled by such explanations, but they are all wrong. The value of this changes depending on where the function is used. But there is always a principle that this in JS represents the main body of the current behavior . In JS, the main research is this in the function, but it does not mean that this is only in the function, this is actually in the function The binding that happens when it is called, what it points to depends entirely on where the function was called . How to distinguish this?

 

3. Who is this


This should be discussed on a case-by-case basis, and there are five common cases:

1. When the function is executed, first check whether there is a "." in front of the function name. If there is, who is in front of ".", this is who; if not, this is window

function fn(){
  console.log(this);
}

var obj={fn:fn};
fn();//this->window
obj.fn();//this->obj

function sum(){
     fn();//this->window
}

sum();

var oo={
 sum:function(){
 console.log(this);//this->oo
       fn();//this->window
  }
};

oo.sum();

 

2. This in the self-executing function is always window

  (function(){ //this->window })();
  ~function(){ //this->window }();

 

3. Bind a method to an event of the element. When the event is triggered, execute the corresponding method. The this in the method is the current element, except that attachEvent is used in IE6~8 (a famous bug in IE)

  oDiv.onclick=function(){
     //this->oDiv
  };
 oDiv.addEventListener("click",function(){
     //this->oDiv
  },false);
oDiv.attachEvent("click",function(){
       //this->window
  });

Most of the time, we encounter event binding, such as the following example, it is not necessary to use attachEvent under IE6~8

function fn(){
  console.log(this);
}

document.getElementById("div1").onclick=fn;//This in fn is #divl
document.getElementById("div1").onclick=function(){
console.log(this);//this->#div1
fn();//this->window
};

 

4. In the constructor mode, this in this.xxx=xxx that appears in the class (in the function body) is an instance of the current class

function CreateJsPerson(name,age){
//The object created by the browser by default is our instance p1->this
this.name=name;//->p1.name=name
this.age=age;
this.writeJs = function(){
console.log("my name is"+this.name +",i can write Js");
};

//The browser returns the created instance by default
}
var p1=new CreateJsPerson("Yin Huazhi",48);

It must be noted that for a certain attribute value (method) in the class, the this in the method needs to see whether there is a "." in front of the method when the method is executed, in order to know who this is . You may wish to look at the following example to understand what it means.

function Fn(){
this.x=100; //this->f1
this.getX=function(){
console.log(this.x);//this->You don't know until getX is executed
} 

}

var f1=new Fn;
f1.getX();//-> this in the method is f1, so f1.x=100
var ss=f1.getX;
ss();//->this in the method is window ->undefined

 

5.call, apply and bind

Let's first look at a question. How to implement this binding obj in the following example?

var obj={name:"boating in the waves"};
function fn(){
console.log(this);//this=>window
}
fn();
obj.fn();//->Uncaught TypeError:obj.fn is not a function

If you bind obj.fn() directly, the program will report an error. Here we should use fn.call(obj) to implement this binding obj. Next, we will introduce the call method in detail:

① First, we let the call method on the prototype execute. When executing the call method, we make this in the fn method become the first parameter value obj; and then execute the fn function.

② Call can also pass values. In strict mode and non-strict mode, the value obtained is different.

// in non-strict mode

var obj={name:"boating in the waves"};

function fn(num1,num2){
console.log(num1+num2);
console.log(this);
}

fn.call(100,200);//this->100 num1=200 num2=undefined
fn.call(obj,100,200);//this->obj num1=100 num2=200
fn.call();//this->window
fn.call(null);//this->window
fn.call(undefined);//this->window
// in strict mode
fn.call();//this->undefined in strict mode
fn.call(null);// in strict mode this->null
fn.call(undefined);//this->undefined in strict mode

The only difference between the two is that when call passes parameters to fn, it passes values ​​one by one, while apply does not pass them one by one, but puts the parameter values ​​to be passed to fn uniformly in an array for operation. But it is also equivalent to assigning values ​​to the formal parameters of fn one by one. To sum up a sentence: the second parameter of call begins to accept a parameter list, and the second parameter of apply begins to accept an array of parameters

fn.call(obj,100,200);
fn.apply(obj,[100,200]);
bind: This method is incompatible under IE6~8. Similar to call/apply, it is used to change the this keyword , but it is obviously different from the two:
fn.call(obj,1,2);//->Change this and execute the fn function are completed together
fn.bind(obj,1,2);//-> just changed this in fn to obj, and passed two parameter values ​​1, 2 to fn,
But at this time, the function fn is not executed
var tempFn=fn.bind(obj,1,2);
tempFn(); //This will execute the fn function
 var a ={
        name : "Cherry",
        fn : function (a,b) {
            console.log( a + b)
        }
    }
  var b = a.fn;
  b.bind(a,1,2)

The above code is not executed, bind returns a function that changes the context, we have to call it manually:

 b.bind(a,1,2)() //3

One thing must be stated: In the fifth case (call apply and bind), all the first four give way.

 

4. The arrow function this points to


Arrow functions use an "arrow" (=>) as the name suggests to define a new syntax for functions, but they are superior to traditional functions for two main reasons: shorter functions and no binding to this .

var obj = {
birth: 1990,
getAge: function () {
var b = this.birth; // 1990
var fn = function () {
return new Date().getFullYear() - this.birth; // this points to window or undefined
} };
return fn();
} }
};

Now, the arrow function completely fixes the pointing of this. The arrow function does not have its own this. The this of the arrow function is not determined when it is called, but the object it is in when it is defined is its this .

In other words, the this of the arrow function depends on whether there is a function in the outer layer. If there is, the this of the outer function is the this of the inner arrow function. If not, this is the window .
   

<button id="btn1">Test arrow function this_1</button>
<button id="btn2">Test arrow function this_2</button>
<script type="text/javascript">
let btn1 = document.getElementById('btn1');
let obj = {
name: 'kobe',
age: 39,
getName: function () {
btn1.onclick = () => {
console.log(this);//obj
} };

}

} };

obj.getName();
</script>

In the above example, since the arrow function does not create its own this, it only inherits this from the upper level of its own scope chain. In fact, it can be simplified to the following code:

  let btn1 = document.getElementById('btn1');
        let obj = {
            name: 'kobe',
            age: 39,
            getName: function () {
                console.log(this)
            }
        };

   obj.getName();

If there is no function in the upper layer, who does this point to?

<button id="btn1">Test arrow function this_1</button>
<button id="btn2">Test arrow function this_2</button>

<script type="text/javascript">
let btn2 = document.getElementById('btn2');
let obj = {
name: 'kobe',
age: 39,
getName: () => {
btn2.onclick = () => {
console.log(this);//window
} };
} 
} };

obj.getName();
</script>

In the above example, although there are two arrow functions, this actually depends on the outermost arrow function. Since obj is an object rather than a function, this points to the Window object.

Since this has been bound in the arrow function according to the lexical scope, when calling the arrow function with call() or apply(), this cannot be bound, that is, the first parameter passed in is ignored :

var obj = {
birth: 1990,
getAge: function (year) {
var b = this.birth; // 1990
var fn = (y) => y - this.birth; // this.birth is still 1990
return fn.call({birth:2000}, year);
} 
};
obj.getAge(2018); // 28