preface
This paper adopts the total score to narrate:
conclusion
If you want to determine the this binding of a running function, you need to find where the function is called directly. Once found, we can apply the following four rules in sequence to determine the object bound to this
-
Called by new? Bind to the newly created object
-
Called by call or apply (or bind)? Binds to the specified object
-
Called by a context object? Bind to that context object
-
Default: bind to undefined in strict mode, otherwise bind to global object
-
The arrow function in ES6 inherits the this binding from the outer function call
A picture is worth a thousand words:
A description
The correctness of the summary will be verified with pseudocode below
bynew
Call? Bind to the newly created object
So, by the way, what does the new operation do?
- Create (or construct) a brand new object
- The new object will be executed
[[prototype]]
The connection - This new object will be bound to the function call
this
- If the function returns no other object, then
new
The function call in the expression automatically returns the new object
function Man(n, a) {
this.name = n
this.age = a;
}
const xueyue = new Man('snow month'.18);
console.log( xueyue.name ); / / month on snow
console.log( xueyue.age ); / / 18
Copy the code
bycall
orapply
(orbind
) call? Binds to the specified object
const { log } = console
const name = 'snow month'
const age = 18
const obj = {
name: 'Anne'.objAge: this.age,
fun: function (like, dislike) {
log(this.objAge)
log(this.name + 'this year' + this.age + ', '.'like' + like + 'Don't like' + dislike)
},
}
const a = { name: 'Flying Tigers'.age: 19 }
const b = { name: 'Night Rose'.age: 20 }
const c = { name: 'Avengers'.age: 21 }
obj.fun.call(a, 'thor'.'the black dragon') // Undefined flying Tigers are 19 years old and prefer Thor to Black Dragon
obj.fun.apply(b, ['Fire Unicorn'.'无影']) // Undefined night rose is 20 years old and prefers fire Unicorn to shadow
obj.fun.bind(c, 'Barrett'.'AWM') ()// Undefined Avenger 21 years old like Barrett doesn't like AWM
Copy the code
Called by a context object? Bind to thatContext object
Many functions in third-party libraries, as well as many new built-in functions in the JavaScript language and host environments, provide an optional argument, often called context, that serves the same purpose as bind(..) Also, make sure your callback uses the specified this
function foo(el) {
console.log(el, this.id);
}
var obj = {
id: 'awesome'
};
/ / calls foo (..) Bind this to obj
[1.2.3].forEach(foo, obj);
// 1 awesome
// 2 awesome
// 3 awesome
Copy the code
Default: in strict mode bound toundefined
Otherwise, bind toGlobal object
function foo() {
console.log(this.a);
}
a = Awesome!;
foo(); / / 666
Copy the code
function foo() {
'use strict';
console.log(this.a);
}
a = Awesome!;
foo(); // TypeError: Cannot read property 'a' of undefined
Copy the code
Note: If you pass null or undefined as a binding object for this to call, apply, or bind, these values will be ignored and the default binding rules will apply:
function foo() {
console.log(this.a);
}
var a = 2;
foo.call(null); / / 2
Copy the code
So when do you pass null?
A very common approach is to use apply(..) To “expand” an array and pass it as a parameter to a function. Similarly, bind(..) Parameters can be currified (pre-set some parameters), which is sometimes very useful
function foo(a,b) {
console.log('a:' + a + ', b:' + b);
}
// Expand the array into parameters
foo.apply(null[2.3]); // a:2, b:3
/ / use the bind (..) Let's do that
var bar = foo.bind(null.2);
bar(3); // a:2, b:3
Copy the code
However, always using NULL to ignore the this binding can have some side effects. If a function does use this (such as a function in a third-party library), the default binding rule binds this to the global object (which in the viewer is a Window), which can have unpredictable consequences (such as modifying the global object).
Obviously, this approach can lead to many bugs that are difficult to analyze and track
A “safer” approach is to pass in a special object (DMZ empty object) to which binding this won’t cause any side effects to your program
function foo(a,b) {
console.log('a:' + a + ', b:' + b);
}
// Our DMZ empty object
var _dmz = Object.create(null);
// Expand the array into parameters
foo.apply(_dmz, [2.3]); // a:2, b:3
/ / use the bind (..) Let's do that
var bar = foo.bind(_dmz, 2);
bar(3); // a:2, b:3
Copy the code
ES6
The arrow function inherited from the outer function callthis
The binding
function foo() {
setTimeout(() = > {
// This in this method inherits from foo()
console.log(this.a);
}, 100);
}
var obj = {
a: 18
};
foo.call(obj); / / 18
Copy the code
Equivalent to ES5 below
function foo() {
var self = this; // lexical capture of this
setTimeout( function (){
console.log(self.a);
}, 100);
}
var obj = {
a: 18
};
foo.call(obj); / / 18
Copy the code
The end of the
Let’s do a little bit of a test
function Parent() {
this.a = 1
this.b = [1.2.this.a]
this.c = { demo: 5 }
this.show = function () {
console.log(this.a, this.b, this.c.demo)
}
}
function Child() {
this.a = 2
this.change = function () {
this.b.push(this.a)
this.a = this.b.length
this.c.demo = this.a++
}
}
Child.prototype = new Parent()
var parent = new Parent()
var child1 = new Child()
var child2 = new Child()
child1.a = 11
child2.a = 12
parent.show() // 1 [1, 2, 1] 5
child1.show() // 11 [1, 2, 1] 5
child2.show() // 12 [1, 2, 1] 5
// This involves knowledge of the prototype chain
child1.change() // b = [1, 2, 1, 11] a = 5 demo = 4
child2.change() // b = [1, 2, 1, 11, 12] a = 6 demo = 5
parent.show() // 1 [1, 2, 1] 5
child1.show() // 5 [1, 2, 1, 11, 12] 5
child2.show() // 6 [1, 2, 1, 11, 12] 5
Copy the code