What is this

Js this refers to the execution body of the function.

Note that only when the function is executed can we determine who this refers to.

Summary of scenarios involving this pointing problem

1. Event binding

A function that binds the event behavior of a Dom element to a function that executes when the event behavior is triggered. This in the function is usually the element of the current operation.

Example: We bind a function to the document click event, and then click the page with the mouse to trigger the event handler. The code is as follows:

// 1 use 'onxxx' to bind functions to it
document.onclick = function() {
    console.dir(this);  // #document
}

// 2 bind functions to it using event listeners
document.addEventListener('click'.function() {
    console.log(this); // #document
});

// 3 Special case: Internet Explorer 6 to 8 bind events in attachEvent mode
document.body.attachEvent('onclick'.function () {
    // this -> window/undefined
});
Copy the code

2. Ordinary functions

When ordinary functions are executed, it is mainly to see if there is a. In front of the function call statement.

  • If you have., then.This points to whoever is in front of it.
  • If there is no., so this points to window/undefined

Window/undefined undefined in non-strict mode: undefined in strict mode: undefined

Ordinary function execution also has several special properties:

  1. A self-executing function, which normally refers to window/undefined.
  2. This is a callback that normally refers to window/undefined. However, you can change this if you do something special inside the function while executing the callback.
  3. In parenthesis expressions, only the last item is taken out to execute. If the last item is a function, this refers to window/undefined when the function executes. If the last item is not a function, an error is reported.

Example: Normal function execution, see if there is one in front.

const fn = function fn(x, y) {
    console.log(x, y, this);
};
fn(10.20)
// 10 20 Window -- "This points to Window

let obj = {
    name: 'zhufeng'.fn: fn
};
obj.fn(10.20)
// 17 17 {name: "xiaoming", fn: ƒ}
// fn(10, 20) is preceded by., and obj
// "this refers to obj

Copy the code

Example: Execute the same method, but this points to a different method

let arr = [];
arr.push(2); // This ->arr in push method
arr.__proto__.push(); //this->arr.__proto__
Array.prototype.push(); //this->Array.prototype

Copy the code

Example: This in the callback function

1. Pass one function as an argument to another function

const fn = function fn(callback) {
    // callback -> The function passed in
    callback();  // This ->window in the callback
    callback.call(1); // Change this to this->1
};

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

The second argument to the forEach() method can specify this in the callback function
let arr = [10.20.30],
    obj = {
        name: 'zhufeng'
    };

arr.forEach(function (item) {
    console.log(item, this); //window
});
arr.forEach(function (item) {
    console.log(item, this); //obj
}, obj);
Copy the code

Case in point: Parenthesis expressions are rarely used in real world development anymore

'use strict'

const fn = function fn(x, y) {
    console.log(x, y, this);
};
let obj = {
    name: 'zhufeng'.fn: fn
};

(10.20, obj.fn)();
// The last item, obj.fn, is a function that can be executed
// Output in non-strict mode:
// undefined undefine Window
// Output in strict mode:
// undefined undefined undefined

// (10, obj.fn, 30)();
// Uncaught TypeError: (10, obj.fn, 30) is not a function
Copy the code

This in the callback of timer setTimeout

let obj = {
    name: 'Ming'.// Add a function type value to the object's property
    Fn :function(){}
    // This function has no prototype object
    fn() {
        console.log(this);  // obj {name: "xiao ", fn: ƒ}
        setTimeout(function () {
            this.name = 'Paul peng wang';
            console.log(this); // Window {name: "Window "... }
            console.log(obj); // {name: "小明", fn: ƒ}
        }, 1000); }}; obj.fn();Copy the code

In the setTimeout callback, we want to change the name property of obj to ‘Wang Peng’.

// Option 1: use the variable _this to cache this in the context of fn() execution
let obj = {
    name: 'Ming'.fn() {
        console.log(this); // {name: "小明", fn: ƒ}
        let _this = this;
        setTimeout(function () {
            _this.name = 'Paul peng wang';
            console.log(this); // this->window
        }, 1000); }}; obj.fn();Copy the code
// Option 2: setTimeout callback function, using the arrow function form
let obj = {
    name: 'Ming'.fn() {
        console.log(this); // this -> {model: ƒ}
        setTimeout(() = > {
            this.name = 'Paul peng wang';
            console.log(this); // this-> {model: ƒ}
            console.log(obj); // obj -> {name: "王鹏", fn: ƒ}
        }, 1000); }}; obj.fn();Copy the code

3. Constructor “add new keyword before function execution”

His in the constructor body points to an instance of the current constructor/class. The core difference between constructor execution and normal function execution is whether the new keyword is used.

4. Arrow functions (with block-level context)

The arrow function (with block-level context) does not have its own this; it uses this in its parent context “host environment”.

The arrow function is nice, but don’t mess with it

  • I’m not going to do THIS, I’m going to do whatever I want,
  • But when it comes to THIS question, take three or four

Example: Arrow functions do not have their own this

let obj = {
    name: 'Ming'.fn: () = > {
        console.log(this); }}; obj.fn();//this -> window
obj.fn.call(100); //this -> window

Copy the code

4. Force the this pointer to change

Prototype call/apply/bind (); / / call/apply/bind (); / / call/apply/bind ()

Using these three methods on arrow functions also won’t work because they don’t have their own this.

The difference between the three

  1. The apply () and call ()
  • Similarities: bothExecute the function that calls them immediatelyAnd change the this pointer in this function.
  • Differences: The only difference is the way the parameter is passed. Call () is passed to a function item by item. Apply () requires all parametersMust be an arrayWrite, but you end up with the same result, which is also passed to the function item by item.
  1. apply() , call() vs bind()
    • What apply() and call() have in common is that the functions that call them are immediately executed.
    • Bind () is a preprocessor that doesn’t execute the functions that call them immediately, just preprocesses this and stores the information beforehand.

Case study:

There is code as follows, which meets the following requirements:

  1. After clicking the document element, the fn function is executed, passing it 10,20.
  2. And let this in the fn function refer to obj
const fn = function fn(x, y) {
    console.log(x, y, this);
}
let obj = {
    name: 'I am the obj
}


document.onclick = fn();
// => x, y, this : undefined, undefined, window
// This means that fn is executed,
// Assign the return value to the document click event


document.onclick = fn(10.20);
// => x, y, this : 10, 20, window
// This is not the case.
// This means that fn takes arguments 10 and 20 and executes it
// Assign the return value to the document click event

document.onclick = fn;
// Fn is executed when the document click event is triggered
// This in fn refers to the element being manipulated, i.e., document,
// Browsers also pass an argument to event functions by default: an event object. This argument is accepted by default by the first parameter in the function.
// => x, y, this : MouseEvent {... }, undefined, document
// Do not meet the requirements

document.onclick = fn.call(obj, 10.20 );
// => x, y, this: ƒ}
// This points to obj
// Call () executes the function immediately, which is not desirable
// Do not meet the requirements

document.onclick = obj.fn(10.20);
// Prerequisite: add an attribute of fn to obj, whose attribute value is fn function
// Write this to meet the requirements
// => x, y, this: ƒ}


document.onclick = function(ev) {
    // fn(); // this : window
    fn.call(obj, 10.20); // this: obj
}
// Write this to meet the requirements
// when the document click event is triggered, the anonymous function is executed,
// Execute fn in an anonymous function based on call(), change this to obj, and pass the parameter.
// => x, y, this: ƒ}

document.onclick = fn.bind(obj, 10.20 );
// Write this to meet the requirements
// => x, y, this: ƒ}
// Bind () prehandles this. Bind () does not execute the function immediately; it just preprocesses the information.
// Bind () is executed, and its return value is an anonymous function that already stores the parameters passed by this and so on.

Copy the code

Write a mock bind method

The bind method: handles this up front and does not execute the function we need to execute right away. Such as the binding method for click events in the case. A method that requires the binding to be executed after the click has occurred, rather than immediately executing the function.

The idea of a chemical function (preprocessing) is used: the function executes to generate a closure, and the preprocessing stores some values for use in the context of the next level.

const fn = function fn(x, y) {
    console.log(x, y, this);
}
let obj = {
    name: 'I am the obj.fn: fn
}
Function.prototype._bind = function _bind(context, ... params) {
    // This: fn context: obj params is the argument to be passed to the fn function
    let self = this;
    return function(. args) {
        // args: receive arguments such as:
        When the event handler executes, it receives an EV event object
        // Merge parameters
        params = params.concat(args)
        // Based on call, the function is immediately executed and its this reference is changed
        self.call(context, ...params);
    }
}

document.onclick = fn._bind(obj, 10.20);
// 17 17 {id: ƒ}
Copy the code

Mock a call method

const fn = function fn(x, y) {
    console.log(x, y, this);
    return x+y
}
let obj = {
    name: 'I am the obj
    // fn: fn
}

Function.prototype._call = function _call(context, ... params) {
    // If the context receives a value of a primitive type, such as: 1
    // Values of primitive value types cannot be set to attributes, otherwise an error will be reported
    // If null or undefined is received, we call context - > window
    // If we receive a value of another primitive value type, we change the primitive value to its corresponding object type value
    if(context == null ) context = window;
    if(!/^(object|function)$/.test(typeof context)) context = Object(context);

    // this -> fn context -> obj params -> Parameters that need to be passed to fn
    // fn has nothing to do with obj itself
    // To make this -> obj, we need to associate them
    // If obj.fn() is executed this is obj
    // Assign fn to a property of obj.

    // context.fn = fn;
    // context.fn(... params); // x, y, this -> ƒ
    // // We found that there was an extra FN attribute in obj, which changed the original data.
    // // and we don't want this, so we need to remove the new FN attribute
    // delete context.fn;

    // But if the original obj has a fn attribute, if we remove it in this way, the original data will also change
    // This is not the case when we add a property to obj by adding a unique value generated by Symbol as the property name

    let key = Symbol('key'),
    result;
    context[key] = this;
    // result Receives the return value of the functionresult = context[key](... params);delete context[key];

    return result;
}
fn._call(obj, 10.20);
// 17 17 {id: ƒ}
// The Symbol(key) attribute is not available when the console is expanded
Copy the code