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
- Find out where the function is called;
- Find this binding according to this binding rules;
- If there is a new binding it points to the instance object before new;
- If there is a display binding, it points to the object showing the binding;
- If there is a context object, point to it;
- They don’t exist, use default binding, point to window, strictly, point to undefined;
Making: github.com/ComponentTY…