this of JS points to the problem

Keywords: Javascript

this of JS points to the problem

This keyword is one of the most complex knowledge points in javascript. It is a special keyword, which is automatically defined in the scope of all functions. It is often used when writing javascript, but do you really understand this? As a qualified coder, he must not only recite the formula, but also understand the implementation logic. When learning this a long time ago, I made some small summaries and wrote some code to test constantly. I hope these study notes can help you.

this point classification

1.1 default binding

The default state is unbound, which is often used in independent functions.

   function test(){
        console.log(this.a);
    }
    var a = 1111;
	test();
//1111

Analysis output:

There is no object calling test() in front of it. This writing method applies the default binding of this. This points to the global object (in non strict mode), so 1111 is output

extend

In the non strict mode, the test() function will be mounted on the window. this in all functions points to the global object window.

In strict mode: this keyword is prohibited from pointing to the global object, this keyword points to the undefined, and the undefined has no global object. An error is thrown

1.2 implicit binding

The call of a function is triggered on an object, and there is a context object at the call location. The typical form is XXX.fun();

function test(){
    console.log(this.a);
}
var obj = {
    a:1111,
    test:test
}
obj.test();
//1111

Analysis output results

The test() function is declared externally and does not strictly belong to obj. However, when calling test(), the calling location will use the obj context to reference the function. Implicit binding will bind this in the function call, that is, this in test(), to this context object (obj).

  • Always remember one sentence: this always points to the object that was invoked at last. Only the last layer in the object property chain affects the location of the call
function test(){
    console.log(this.a);
}
var obj1 = {
    a:1111,
    test:test
}
var obj2 = {
    a:2222,
    obj1:obj1
}
obj2.obj1.test();
//1111,this points to obj1 to which it was last bound

Note that implicit loss often occurs in implicit binding

Q: What is implicit loss?

A: The bound function is missing the bound object.

attention: parameter passing is actually an implicit assignment

function fn(){
    console.log(this.a);
}
var a = 'hello world';
var obj = {
    a:'obj',
    fn:fn
}
var demo = obj.fn;
demo();
//The console finally prints hello world

Analysis output results

The demo directly points to the reference of fn, and the call has nothing to do with obj. The demo is bound with the reference of fn function, so the demo is just a function call. The default binding is applied and the global object is bound. So print out hello world.

  • Confusing knowledge points: XXX.fn(); If there is nothing before fn(), it is not an implicit binding.

In addition to the above loss, implicit binding loss often occurs in callback functions (event callback is also one of them)

function test(){
 	console.log(this.a);
}
function gn(fn){
    fn();
}
var obj = {
    a:1111,
    test:test
}
var a = 'I am a global object'
gn(obj.test);
//I am a global object

Once again, parameter passing is also an invisible assignment

  • Special case: timer setTimeout
function test(){
    console.log(this.a);
}
var a = 'hello world';
var obj = {
    a:'obj',
    test:test
}		
setTimeout(obj.test,0);
//hello world

Output result analysis

The first parameter of setTimeout is the incoming callback function. obj.test is bound as a function, which is equivalent to

function setTimeout(test,delay){//delay execution in milliseconds
    test();//obj.test
}
1.3 explicit binding

Directly specify this binding object with the help of apply,call,bind and other methods. The first parameter of call, apply and bind is the object pointed to by this of the corresponding function.

Q: What's the difference between apply and call.bind?

A: The functions of apply and call are the same, and the methods of passing parameters are different. The corresponding functions will be executed, but bind is different. It will not be executed and needs to be called manually.

//apply binding
function test(){
    console.log(this.a);
}
var obj = {
    a:1111,
}
test.apply(obj);//hello world

//call binding
function test(){
    console.log(this.a);
}
var obj = {
    a:1111,
}
test.call(obj);//hello world

Does explicit binding prevent binding loss? Of course not

  • Explicit binding also faces the problem of binding loss
function sayHi(){
    console.log('hello',this.name);
}
var obj = {
    name:'Gabrielle',
    sayHi:sayHi
}
var name = 'Gaby';
var Hi  = function(fn){
    fn();
}
Hi.call(obj,obj.sayHi);
//hello,Gaby

Output result analysis

When fn is executed, it is equivalent to directly calling the sayHi method (obj.sayHi has been assigned to fn, and the implicit binding has been lost). The value of this is not specified, and the default binding is corresponding. So the output is hello Gaby.

Q: How to solve the problem of binding loss?

A: Hard bind this to fn.

function sayHi(){
    console.log('hello',this.name);
}
var obj = {
    name:'Gabrielle',
    sayHi:sayHi
}
var name = 'Gaby';
var Hi  = function(fn){
    fn.call(this);
}
Hi.call(obj,obj.sayHi);
//hello,Gabrielle

Output result analysis

Because obj is bound to this in the Hi function, fn binds this function to the sayHi function.

It can also be rewritten with bind, which is classified as hard binding.

function sayHi(){
    console.log('hello',this.name);
}
var obj = {
    name:'Gabrielle',
    sayHi:sayHi
}
var name = 'Gaby';
var Hi  = sayHi.bind(obj);
Hi.call(obj,obj.sayHi);
//hello,Gabrielle

  • Binding exception

Apply binding rules: null and undefined are passed into call, apply and bind as binding objects. These values are often ignored

var obj = {
    name:'Gabrielle'
}
var name = 'Gaby';
function Hi() {
    console.log(this.name);
}
Hi.call(null);
//Gaby

1.4 new binding

js has no class. Simulate the class through the constructor and call the function with new. The following operations will be performed automatically

Create an object. The object is connected by [[Prototype]]. The new object will be bound to this of the function call. If the function does not return other objects, the new object will be returned. Otherwise, the object returned by the constructor will be returned.

  • Handwritten new
  //1. Create an empty object
function _new(fn,...args){
  //2. Of this object__ proto__  The prototype object that points to fn this constructor
  var obj = Object.create(fn.prototype);
  //3. Change this point
  var res = fn.apply(obj,args);
  // 4. If the result returned by the constructor is a reference data type, the result after running will be returned; otherwise, the newly created obj will be returned
  if ((res!==null && typeof res == "object") || typeof res == "function") {
      return res;
  }
  return obj;
}
  • new bind this
function person(name){
    this.name = name;
}
var per = new person('Gaby');
console.log(per.name);
//Gaby
//per has been bound to this called by person


Insert a small clip
When writing the above code, I forgot to pass in the parameter to person(), resulting in the console printing undefined. Then I was thinking about a question, why can I have normal output only by passing in parameters to person(). What is the logic behind this? Here's a reminder. function person() is a constructor. this.name wants to get the name, but my name is not passed in. The name I want to pass in the constructor is not obtained in the person object of new. Because there are no parameters passed in, it can't be assigned at all.

1.5 arrow function this binding

The ES6 arrow function needs to be discussed separately. It does not apply to the above four binding rules. There is no this in the arrow function

The direction of this in the arrow function depends on this in the outer scope. Who the outer scope or this in the function points to will point to.

function fn(){
    return () =>{
        console.log(this.name);
    }
}
let obj1 = {
    name='Gabrielle'
};
let obj1 = {
    name='Gaby'
}
let bar = fn.call(obj1);//fn this points to obj1
bar.call(obj2);
//Gabrielle

Q1: why is it not modified after binding again?

A2: characteristics of cuz arrow function: once this binding of the arrow function is successful, it cannot be modified again.

Q2: really can't it be modified?

A2: in fact, there is no solution. The arrow function this will be found from the upper scope like scope inheritance, so you can modify the outer function this to indirectly modify the arrow function this.

function fn(){
    return () =>{
        console.log(this.name);
    }
}
let obj1 = {
    name='Gabrielle'
};
let obj1 = {
    name='Gaby'
}
fn.call(obj1)();//fn this points to obj1, and the arrow function this also points to obj1
fn.call(obj2)();//fn this points to obj2, and the arrow function this also points to obj2

keep going

There are many and miscellaneous knowledge points to be sorted out for JS in-depth understanding. To learn JS well, we must understand the logic and principles behind it, rather than simply know how to write code to use.

Posted by Wakab on Mon, 04 Oct 2021 15:26:44 -0700