this points to the problem

Posted by yuppicide on Sun, 30 Jan 2022 16:19:19 +0100

What are the pointer functions that change the this pointer inside a function and what are their differences?
What are the directions for this?
Code Analysis Questions:

var name = 'window'

var person1 = {
  name: 'person1',
  show1: function () {
    console.log(this.name)
  },
  show2: () => console.log(this.name),
  show3: function () {
    return function () {
      console.log(this.name)
    }
  },
  show4: function () {
    return () => console.log(this.name)
  }
}
var person2 = { name: 'person2' }

person1.show1()
person1.show1.call(person2)

person1.show2()
person1.show2.call(person2)

person1.show3()()
person1.show3().call(person2)
person1.show3.call(person2)()

person1.show4()()
person1.show4().call(person2)
person1.show4.call(person2)()


1. Direction of this


Baidu and Google input the keyword "this direction". There must be thousands of articles. It is not necessary to read all the articles in order to master them in all aspects and without dead ends. So it's better to comb out a solid frame and fill it with our thoughts.

Mind Mapping


The essence of this section:
this always points to an object (in non-strict mode) and specifies which object is dynamically bound at runtime based on the execution environment of the function, not when the function is declared;
In addition to the less commonly used cases of with and eval, this can be roughly divided into four types for practical use:
Method calls as objects;
Called as a normal function;
Constructor call;
Call or apply call;
In the arrow function, this points to this in the upper scope of the function.
The difference between a constructor and a normal function is how it is called.
A, call(B) => can be understood as calling method A within the scope of B;
Analysis
1. Method Calls as Objects

this points to a function when its method as an object is called

var obj = {
    a: 'yuguang',
    getName: function(){
        console.log(this === obj);
        console.log(this.a);
    }
};

obj.getName(); // true yuguang


2. Call as a normal function

When a function is called not as a property of an object, but as a normal function, this always points to a global object (usually a Window object in a browser)

window.name = 'yuguang';

var getName = function(){
    console.log(this.name);
};

getName(); // yuguang


Or this confusing code:

window.name = 'King'
var obj = {
    name: 'yuguang',
    getName: function(){
        console.log(this.name);
    }
};

var getNew = obj.getName;
getNew(); // King


In strict mode in ES5, this is specified not to point to global objects, but to undefined

3. Constructor Calls

With the exception of some built-in functions, most functions in Js can be constructors, and they are no different from normal functions

The difference between a constructor and a normal function is how it is called:
When the new operator calls a function, it always returns an object, which this usually points to

var MyClass = function(){
    this.name = 'yuguang';
}
var obj = new MyClass();
obj.name; // yuguang


However, if an object object is explicitly returned, the result of this operation will eventually return that object.

var MyClass = function () {
    this.name = 1;
    return {
        name: 2
    }
}
var myClass = new MyClass(); 
console.log('myClass:', myClass); // { name: 2}


This is not an issue as long as the constructor returns no data or data of a non-object type.

4. call or apply calls

call and apply can dynamically change this of a function compared to normal function calls

var obj1 = {
    name: 1,
    getName: function (num = '') {
        return this.name + num;
    }
};

var obj2 = {
    name: 2,
};
// It can be understood that obj1 was called in the scope of obj2. GetName() function
console.log(obj1.getName()); // 1
console.log(obj1.getName.call(obj2, 2)); // 2 + 2 = 4
console.log(obj1.getName.apply(obj2, [2])); // 2 + 2 = 4


5. Arrow function

The arrow function does not create its own this, it only inherits it from the upper level of its scope chain.

Therefore, in the following code, this value in the function passed to setInterval is the same as that in the closed function:

this.val = 2;
var obj = {
    val: 1,
    getVal: () => {
        console.log(this.val);
    }
}

obj.getVal(); // 2


Common pits
Like a title, sometimes this points to undefined

Situation One

var obj = {
    name: '1',
    getName: function (params) {
        console.log(this.name)
    }
};
obj.getName();

var getName2 = obj.getName;
getName2();


At this point, when getName2() is called as a normal function, this points to the global object, window.

Situation 2

When we want to simplify our code by encapsulating the Dom method ourselves:

var getDomById = function (id) {
    return document.getElementById(id);
};
getDomById('div1') //dom node


So let's see if that's okay?

var getDomById = document.getElementById
getDomById('div1') // Uncaught TypeError: Illegal invocation


This is because:

When we call the method of the document object, this inside the method points to the document.
When we use getId to apply a method within a document and call it as a normal function, the this of the function content points to the global object.
Use call and apply to fix scenario two

document.getElementById = (function (func) {
    return function(){
        return func.call(document, ...arguments)
    }
})(document.getElementById) 
// Save document in scope with immediate execution function


2. call and apply


Don't resist it because of its "power". Understand and become familiar with it is something we have to do, reluctantly!

Mind Mapping
1. The difference between call and apply
Let's start with the difference, because they are almost identical, so the code instances call and apply below can be easily switched.

When they are designed to do the same thing, the only difference is that the format of the arguments is different

apply accepts two parameters
The first parameter specifies the direction of the this object within the function body
The second parameter is a subscripted set of parameters (which can be an array or an array of classes)
call accepts parameters that are not fixed
The first parameter specifies the direction of the this object within the function body
The second and subsequent parameters for the function call
Because in all (non-arrow) functions, the parameters of the function can be referenced in the function through the arguments object. This object contains every parameter passed to the function and is itself an array of classes, which we apply use more often.

Calls are grammatical sugars wrapped on apply, so if we know the number of parameters explicitly and want to show them, we can use call.

When using call or apply, if the first parameter we pass in is null, this in the function body will point to the host object by default, and window in the browser.

Method of borrowing other objects

We can pass null directly instead of any object

Math.max.apply(null, [1, 2, 3, 4, 5])


2. What can call and apply do?
Call a function with a specified this value and one or more parameters given separately -- MDN

Call the constructor to implement inheritance;
this calls the function and specifies the context;
Call the function without specifying the first parameter;
1. Call the constructor to implement inheritance

To achieve the effect of inheritance by borrowing:

function Product(name, price) {
    this.name = name;
    this.price = price;
}

function Food(name, price) {
    Product.call(this, name, price); //
    this.category = food;
}

var hotDog = new Food('hotDog', 20);


2. Call the function and specify this context

this is pointed at obj

function showName() {
    console.log(this.id + ':' + this.name);
};

var obj = {
    id: 1,
    name: 'yuguang'
};

showName.call(obj)


3. Simple call to a function using call

Math.max.apply(null, [1,2,3,10,4,5]); // 10


3. Implement a call by simulation


Let's see what call s do to help us?

var foo = {
    value: 1
};
function show() {
    console.log(this.value);
};
show.call(foo); //1


Just like solving equations, to find a breakthrough in a known condition:

call changes the direction of this, pointing to foo;
The show function is executed;
The parameter passed in should be this +parameter list;
First Edition Code

Of the three points mentioned above, only one has been completed, and the parameters passed in

var foo = {
    value: 1
};
function show() {
    console.log(this.value);
};
Function.prototype.setCall = function (obj) {
    console.log(this); // this point to show
    obj.func = this; // To make a function an internal property of an object
    obj.func(obj.value); // Specify function
    delete obj.func // Delete the function, when nothing happens~
}
show.setCall(foo);


Second edition code

To solve the parameter problem, we need to be able to get the parameter and pass it in correctly:

var foo = {
    value: 1
};
function show(a, b) {
    console.log(this.value);
    console.log(a + b);
};
Function.prototype.setCall = function (obj) {
    obj.fn = this; // To make a function an internal property of an object
    var args = [];
    for(let i = 1; i < arguments.length; i++){
        args.push('arguments[' + i + ']');
    }
    eval('obj.fn(' + args + ')'); // Incoming parameters
    delete obj.fn; // Delete the function, when nothing happens~
}

show.setCall(foo, 1, 2); // 1 3


At this point, we can do that, using call with multiple parameters passed in, but if you only want to use one method?

Third edition code

Function.prototype.setCall = function (obj) {
    var obj = obj || window;
    obj.fn = this;
    var args = [];
    for(var i = 1, len = arguments.length; i < len; i++) {
        args.push('arguments[' + i + ']');
      }
      var result = eval('obj.fn(' + args +')');
      delete obj.fn;
      return result;
};
// Test it
var value = 2;
var obj = { value: 1 };

function bar(name, age) {
      console.log(this.value);
      return {
        value: this.value,
        name: name,
        age: age
      }
}
bar.setCall(null); // 2
console.log(bar.setCall(obj, 'yuguang', 18));


IV. Bid


The bind() method creates a new function whose this is specified as the first parameter of bind() when bind() is called, while the rest of the parameters are used as the parameters of the new function for the call - MDN

When you mention call and apply, you can't get around bind. Let's try to simulate a bind method to get a better understanding of it:

Function.prototype.bind = function (obj) {
    var _this = this; // Save functions that call bind
    var obj = obj || window; // Determine which this is being pointed to, and if obj is empty, this execution scope needs top
    return function(){
        return _this.apply(obj, arguments); // Correct this's direction
    }
};

var obj = {
    name: 1,
    getName: function(){
        console.log(this.name)
    }
};

var func = function(){
    console.log(this.name);
}.bind(obj);

func(); // 1

Topics: Javascript