Js this points to

1. What is this?

  1. When you run javascript code on the browser side, this is usually a window in a non-function; So if we study this, we study this in the function. In ES6+, the “this” in the “block-level context” is the “this” in its context, which can also be interpreted as: the block-level context does not have its own this.
  2. This is a pointer to the object on which the function is called. When we say this, we refer to the execution body of a function. So whoever executes this function, this in this function refers to whoever it is. Of course there are special cases, and we’ll talk about that later

2. Several binding rules for this

To distinguish the execution body (this) of a function, we can analyze it in the following ways:

  1. event
  2. Ordinary function execution
  3. Constructor execution (new)
  4. Arrow function execution
  5. Force changes to this based on call/apply/bind

2.1 Event Binding

Whether DOM0 or DOM2 level event binding, binding a method to an element’s event behavior. When the event triggers method execution, this in the method is the current element itself.

Special circumstances:

  1. In IE6~8, DOM2 event binding is implemented based on attachEvent, and the event triggers the execution of the method. This in the method is not the element itself, but in most cases is the window
  2. If the event callback forces a change to this in a function based on call/apply/bind, it will also force the change
/ / 1
document.body.onclick = function () {
    console.log(this) // body
}

/ / 2
document.body.addEventListener('click'.function () {
    console.log(this) // body
})

// 3: call/apply/bind forces this to change
const callback = function() {
    console.log(this) // { name: '123' }
}
document.body.addEventListener('click', callback.bind({ name: '123' }))
Copy the code

2.2 Common Function execution

  1. The function is executed to see if there are “dots” in front of the function. If there is a “dot”, then this is whoever comes before the “dot”, there is no “dot” this is window, “JS strictly mode is undefined”.
// 1: bind this by default
// The default binding, which is used when no other binding rules can be applied, usually stand-alone function calls
function fn() {
    console.log(this)
}
fn()  // window

// 2: implicitly bind this
// The call to a function is triggered on an object, i.e. there is a context object at the call location. The typical form is xxx.fn ()
const obj = { 
    fn: function() {
        console.log(this)
    }
}
obj.fn()  // obj

/ / 3
// xxx.__proto__. Fn () -> This is: xxx.__proto__
Copy the code
  1. Self-executing function execution: this inside a function is usually window, but in strict mode is undefined
  2. This in callback functions is also window/undefined. Unless special handling is done, or normal DOM event binding

2.3 Constructors

When a function is called using the new keyword, this in the function must be the instance object created in the constructor.

The arrow function cannot be used as a constructor, so it cannot be executed with new or an error will be reported

2.4 Arrow Function

The arrow function is new to ES6 and has some differences from normal functions. The arrow function does not have its own this; its this inherits from this in the outer code base.

Note the following when using the arrow function:

  1. When you create the arrow function, you already know its This point
  2. The this inside the arrow function points to the outer THI
  3. Should not be used as a constructor, that is, the new command should not be used, otherwise an error will be thrown
  4. Arrow functions have no arguments objects
  5. Arrow functions do not have their own this, so you cannot use call(), apply(), or bind() to change the this pointer inside the arrow function.
/ / 1
let obj = {
    n: 0.fn() {
        setTimeout(() = > {
            // this -> this -> obj
            this.n++
        }, 1000)
    }
}
obj.fn()

/ / 2
const fn = () = > {
  // This refers to the outer this, in which case this refers to the window
  console.log(this)
}

fn.bind(1) ()// Window
// Arrow functions do not have their own this
// Call (), apply(), bind() cannot be used to change the this pointer inside the arrow function
Copy the code

2.5 Call /apply/bind forces this to change

Call /apply/bind are all used to change the direction of this. If call/apply/bind forces a change to this, it will force the change

  1. Call /apply executes the function immediately, changing THIS in the function, and passing parameter information to the function. The only difference is that the method of passing parameters is different. If a call has multiple parameters passed to a function, they need to be passed in sequence, separated by commas. Apply puts it directly into an array
  2. Bind does not execute the function immediately. It preprocesses the this and the arguments in the function. Bind returns a function. Bind multiple times and only recognize the value of the first bind
function fn(x, y) {
    console.log(this, x, y)
}
let obj = {
    name: 'zhangsan'
}
fn(10.20) // window, 10, 20
fn.call(obj, 10.20) // {name: 'zhangsan'}, 10, 20
fn.apply(obj, [10.20]) // {name: 'zhangsan'}, 10, 20

document.body.onclick = function (e) {
    // Create an anonymous function that is bound to the click event and is executed when the BODY click is triggered
    // This refers to the body element
    console.log(this)}// You can change this by using the bind method. Clicking on the body triggers the event, which points to obj
document.body.onclick = fn.bind(obj, 10.20)
Copy the code

3. Implement call/apply/bind methods

/* * Rewrite the built-in call/apply: associate the function that needs to be executed with the function that needs to be changed. When a normal function is called, the function this refers to whoever executed the function * 2. Inside the call, you assign a property to the incoming object and assign the currently executing function to this property */
Function.prototype.call = function call(context, ... params) {
    // this -> context -> obj params -> [10,20]
    context == null ? context = window : null;
    Context must be a value of object type: only objects can set properties
    !/^(object|function)$/.test(typeof context) ? context = Object(context) : null
    let self = this,
        result = null,
        key = Symbol('KEY') // Add attribute names to ensure uniqueness and prevent contamination of members of the original object
    context[key] = self Obj [key] = fnresult = context[key](... params)// obj.fn()
    delete context[key] // Remove your new attributes when finished
    return result
}

// The only difference between the apply and call types is the way they are passed
Function.prototype.apply = function call(context, params) {
    context == null ? context = window : null;
    !/^(object|function)$/.test(typeof context) ? context = Object(context) : null
    let self = this,
        result = null,
        key = Symbol('KEY') context[key] = self result = context[key](... params)delete context[key]
    return result
}

function fn(x, y) {
    console.log(this, x, y)
}
let obj = {
    name: 'zhangsan'
}
fn.call(obj, 10.20)
fn.apply(0[10.20])
Copy the code
/*
 * 重写内置bind:柯理化思想「预处理思想」
 */
Function.prototype.bind = function bind(context, ... outerArgs) {
    This -> fn context -> obj outerArgs -> [10,20]
    let self = this
    return function (. innerArgs) {
        // innerArgs -> [e]self.call(context, ... outerArgs.concat(innerArgs)) } }function fn(x, y, e) {
    console.log(this, x, y, e)
}
let obj = {
    name: 'zhangsan'
}

document.body.onclick = fn.bind(obj, 10.20)
Copy the code