This point

In this section, we will give a detailed introduction to JS this pointing, introduce how this points in different situations, and supplement how we use this in ES5 and ES6 to help you better understand JS pointing problems.

This in ES5 points to the problem

In ES5, there’s really only one principle for this to point to: this always points to the object that last called it. Let’s look at a few examples:

// 例1
var name = "windowsName";
function a() {
    var name = "Cherry";

    console.log(this.name); // windowsName
    console.log("inner:" + this); // inner: window
}
a();
console.log("outer:" + this); // outer: window
Copy the code

In example 1, the final call to function A is the window object, which is actually equivalent to window.a(). So this refers to the window object.

// 例2
var name = "windowsName";
var a = {
    name: "Cherry".fn : function () {
        console.log(this.name);      // Cherry
    }
}
a.fn();
Copy the code

In example 2, we declare a function called fn on object A, where this refers to object A because the object on which fn is called is A.

// 例3
var name = "windowsName";
var a = {
    name: "Cherry".fn : function () {
        console.log(this.name);      // undefined}}window.a.fn();
Copy the code

In example 3, the object on which the function fn is finally called is A, so this refers to A.

// 例4
var name = "windowsName";
var a = {
    name : null.// name: "Cherry",
    fn : function () {
        console.log(this.name);      // windowsName}}var f = a.fn;
f();
Copy the code

In example 4, we declare a variable f on the window that points to the function fn, so the object that eventually points to fn is the window object.

How do I change the direction of this

To change the direction of this, we can summarize the following methods:

  • Use the arrow function of ES6

  • Use _this = this inside the function

  • Use apply, call, and bind

  • New instantiates an object

/ / case 5
var name = "windowsName";

var a = {
    name : "Cherry".func1: function () {
        console.log(this.name)     
    },

    func2: function () {
        setTimeout(function () {
            this.func1()
        },100); }}; a.func2()// this.func1 is not a function
Copy the code

In example 5, we called the func2 function on object A, expecting it to execute this.func1() 100 milliseconds later, and got this.func1 is not a function. The reason is that the anonymous function executed in Func2 after 100 milliseconds does not specify this to which it points, so it points to the window object by default. The window object does not have a function named func1, so an error is reported.

Using Example 5 as a demo, we will modify the code to produce the desired results using the various methods mentioned above.

Arrow function

The arrow function was introduced in ES6 and can be used to avoid the this pit in ES5. The principle behind the arrow function is simple: the arrow function’s this always points to this when the function is defined, not when it is executed.

Let’s rewrite example 5:

var name = "windowsName";

var a = {
    name : "Cherry".func1: function () {
        console.log(this.name)     
    },

    func2: function () {
        setTimeout(() = > {
            this.func1()
        },100); }}; a.func2()// Cherry
Copy the code

In the code above, we changed the execution function of the setTimeout function in func2 from an anonymous function to an arrow function. The this of the arrow function always points to the this that created the definition of it, func2; that is, the this of the arrow function points to the This of func2. Here object A calls func2, so func2’s this points to A, and this in the arrow function also points to A.

Let’s change the above call:

.const funcA = a.func2;
funcA(); // this.func1 is not a function
Copy the code

Then we find that the result is wrong again, why is this? The reason lies in the This direction of Func2. We know that the arrow function has the same this as the function that created it (func2). But in our example, we created a variable funcA, pointed to the function func2 in object A, and called funcA. Func2’s This points to the window object, not object A.

Let’s rewrite this:

var a = {
    name : "Cherry".func2: function () {
        setTimeout(() = > {
            console.log(this);
        },100); }};const funcB = a.func2;
funcB(); // window
Copy the code

So the arrow function’s this depends on the outer this.

What happens when arrow functions are nested inside? Let’s look at an example:

var a = {
    name : "Cherry".func1: function () {
        console.log(this.name)     
    },

    func2: function () {
        setTimeout(() = > {
            const fn = () = > {
                this.func1();
            };
            fn()
        },100); }}; a.func2();// Cherry;
Copy the code

We find that when the arrow function is nested, the this of the inner arrow function points to the same point as the this of the outer arrow function.

Declare a variable pointing to the current this object

We can declare a variable _this to hold the this value of the current function, and then replace this with _this when the function is called, so that the reference to this does not change depending on the object on which it is called.

var name = "windowsName";

var a = {
    name : "Cherry".func1: function () {
        console.log(this.name)     
    },

    func2: function () {
        const _this = this;
        setTimeout(function () {
            _this.func1()
        },100); }}; a.func2()// Cherry;
Copy the code

Here func2 declares a variable _this, which has the same reference as func2’s this. When we call the anonymous function to find _this.func1(), it no longer points to the window object, but to the object (object A) to which func2’s this points.

Use call, apply, bind

Call, apply, and bind are some of the methods provided in ES5 to modify this Pointers. The effect is the same, but the usage is different.

  • call

fun.call(thisArg[, arg1[, arg2[, …]]])

  • apply

fun.apply(thisArg, [argsArray])

  • bind(this, arg1, arg2, …)

The bind argument receives the same as call, but returns a function rather than executing it. Need to call manually

The difference between apply and bind is that the return value of the call method is a function.

Let’s rewrite Example 5 so that this points to the correct value using call, apply, and bind respectively:

// call
var a = {
    name : "Cherry".func1: function () {
        console.log(this.name)     
    },

    func2: function () {
        setTimeout(function () {
            this.func1();
        }.call(a),100); }};// apply
var a = {
    name : "Cherry".func1: function () {
        console.log(this.name)     
    },

    func2: function () {
        const _this = this;
        setTimeout(function () {
            this.func1();
        }.apply(a),100); }};// bind
var a = {
    name : "Cherry".func1: function () {
        console.log(this.name)     
    },

    func2: function () {
        const _this = this;
        setTimeout(function () {
            this.func1();
        }.bind(a)(),100); }};Copy the code

Let’s look at passing arguments to these three methods. Here’s a small example of a sum:

/ / case 6
var obj = {
    init: 10.fn: function (a,b) {
        return a + b + this.init; }}var obj2 = {
    init: 100,}const fn = obj.fn;
fn(1.2); // NaN
Copy the code

The object on which fn is called is the window, and there is no init on the window, so NaN is output. Let’s use call, apply, and bind to modify the direction of this:

// call
fn.call(obj, 1.2) / / 13
fn.call(obj2, 1.2) / / 103

// apply
fn.apply(obj, [1.2]) / / 13
fn.apply(obj2, [1.2]) / / 103

// bind
fn.bind(obj, 1.2) ()/ / 13
fn.bind(obj2, 1.2) ()/ / 103
Copy the code

With call, apply, and bind, we can specify this to point to and have the function execute correctly.

Call a function using a constructor

We can call the constructor with the new keyword. Let’s look at how new creates a new object.

  1. Create a new object

  2. The new object inherits the prototype of the original function

  3. Bind the new object to the function’s this

  4. If the function returns no other object, the new object is returned

Let’s write a new operator by hand.

// create is a constructor
function create(Con, ...arguments) {
    // Create a new object
    let obj = {};
    // The new object inherits the prototype of the original function
    Object.setPrototypeOf(obj, Con.prototype);
    // Use apply to modify the this reference of the original function, and execute
    let result = Con.apply(obj, arguments);
    // If the result is object, the function is considered to have returned an object, otherwise a new object obj is returned
    return result instanceof Object ? result : obj;
}

function Obj(name, age) {
    this.name = name;
    this.age = age;
}
Obj.prototype.sayName = function() {
    console.log(this.name);
}
const obj = new create(Obj, 'li'.18)
Copy the code

conclusion

In this section, we introduce the pointing problem of this in JS in detail, and explain the difference and use of arrow functions, apply, call and bind. Finally, we added the process of new. We will move on to other CORE JS knowledge.

I am how to celebrate more than years, if the article has helped you, I hope you can point a thumbs-up, thank you!

If you have any questions, please discuss them in the comments section.