What is this? How to understand?

The binding of this is at run time, not at write time, and its context depends on various conditions at the time the function is called. It is independent of the position of the function declaration and the lexical scope of the function. When a function is called, an active record is created. This record contains information about where the function was called (call stack), how the function was called, the parameters passed, etc. This is a property in this record, which is used during the function execution. This refers neither to the function itself nor to the lexical scope of the function.

Rules for the this binding

  • The default binding

    This binds the Window object by default

    function Foo () {
        console.log(this.name)
    }
    var name = '123'
    Foo() // 123 this => window
    Copy the code
  • Implicit binding

    The this binding is bound at run time, not at write time, so we need to look at where the function is called and the various conditions under which it is called. If a context object exists when the function is called, this is automatically bound to that context object.

    function Foo () {
        console.log(this.name)
    }
    var obj = {
        name: 'objFoo'.Foo: Foo
    }
    obj.Foo() // objFoo has context object obj so this => obj
    Copy the code

    It is important to note that only the upper or last level of the object property reference chain is at play in the call location. What you mean?

    function Foo () {
        console.log(this.name)
    }
    var obj = {
        name: 'objFoo'.Foo: Foo
    }
    var oob = {
        name: 'oobFoo'.obj: obj
    }
    oob.obj.Foo() // objFoo refers to the upper or last layer of the chain, which is the object layer obj, so this => obj
    Copy the code

    But there is an implicit loss problem with this binding. What is implicit loss? Why is it missing?

    For example

    This is implicitly lost

    • Function is an alias
    function Foo() {
        console.log(this.name)
    }
    var obj = {
        name: 'objFoo'.Foo: Foo
    }
    var name = 'windowFoo' // The name attribute in the window scope
    var bar = obj.Foo // Function alias
    
    bar() // Instead of printing objFoo, it will print windowFoo
    Copy the code

    Why is that?

    var bar = obj.Foo
    // The function is also an object. The actual obj.Foo takes the reference to Foo in memory and assigns it to bar, so the code should look like this
    var bar = obj.Foo = Foo
    Copy the code

    So executing bar() is actually executing Foo() directly, and has nothing to do with obj. Now Foo has no context object, so this selects the default binding window, so it prints windowFoo.

    • Implicit loss of 2 callback arguments
    function Foo () {
        console.log(this.name)
    }
    function callBack (fn) {
        fn()
    }
    var obj = {
        name: 'objFoo'.Foo: Foo
    }
    var name = 'windowFoo'
    callBack(obj.Foo) // The output is windowFoo again unexpectedly exists
    Copy the code

    Because function passing is really just parameter assignment, the code should read like this

    function callBack(fn = Obj.Foo = Foo) {
        fn() // So Foo is still called directly and obj has nothing to do with it
    }
    Copy the code
    • Implicit loss 3

    Passing a function value to a language built-in function also causes this implicit loss

    function Foo () {
        console.log(this.name)
    }
    var obj = {
        name: 'objFoo'.Foo: Foo
    }
    var name = 'windowFoo'
    setTimeout(obj.Foo, 1100)
    Copy the code

    In fact, the internal implementation principle of setTimeout should be like this, my opinion.

    function setTimeout(fn,delay) {
    	fn.call(null, delay)
    }
    // So the function is still passing arguments
    Copy the code

Explicitly bound

Call, apply, bind

function Foo () {
    console.log(this.name)
}
var obj = {
    name: 'objFoo'
}
var demo = {
    name: 'demoFoo'
}
Foo.call(obj) // objFoo
Foo.call(demo) // demoFoo
Foo.apply(demo) // demoFoo
Foo.apply(obj) // demoFoo
Copy the code

However, call and Apply do not solve the problem of implicit loss of this. In my opinion, call and Apply bind this and call functions immediately. We can also change the direction of this at this point.

The solution to this implicit loss is to hardbind bind

function Foo () {
    console.log(this.name)
}
var obj = {
    name: 'objFoo'
}
var demo = {
    name: 'demoFoo'
}
Foo.bind(obj)
Copy the code

The bind method returns a function that binds this internally. This will remain the same no matter where you call it later.

New binding, highest priority, this refers to the instance object before new

Note: With the new operator, if an object is returned from the constructor, this refers to the object returned; otherwise, this refers to a new object created inside the constructor when the new operation is performed. Hard to understand?

function Person () {
   this.name = "123"
}
let p = new Person()
console.log(p.name)  // In this case this refers to the instance object before new, so print 123

function Person () {
   this.name = "123"
   return{}}let p = new Person()
console.log(p.name) // Here this refers to the returned object {}, so print undefined
Copy the code

Now look at the new binding

function Person () {
    this.name = 'person3'
}
var obj = {
    name: 'person1'
}
var obj2 = {
    name: 'person2'
}
var p1 = new Person
console.log(p1.name) // person3
Copy the code

The unexpected new operator changes the direction of this

// The constructor returns an object
function Person () {
   this.name = '123'
   return{}}let p = new Person()
console.log(p.name)  // undefined
// The constructor returns a function
function Person () {
   this.name = '123'
   return function () {}}let p = new Person()
console.log(p.name) // undefined
// The constructor returns a primitive type
function Person () {
   this.name = '123'
   return 1
}
let p = new Person()
console.log(p.name) / / 123
The new operator makes it clear that returning the basic type does not change the orientation of this. This still points to the new object being created.
function Person () {
   this.name = '123'
   return null
}
let p = new Person()
console.log(p.name) / / 123
Copy the code

Priority comparison

New binding > show binding (call, the apply and bind) > implicit binding (context object) > the default binding (window | | undefined)

conclusion

To find this binding

  1. Find out where the function is called;
  2. Find this binding according to this binding rules;
  3. If there is a new binding it points to the instance object before new;
  4. If there is a display binding, it points to the object showing the binding;
  5. If there is a context object, point to it;
  6. They don’t exist, use default binding, point to window, strictly, point to undefined;

Making: github.com/ComponentTY…