This is one of the most intriguing features in JavaScript, like the tenses in high school English, such as the passive tense, past tense, present tense, past time, and, no matter how many times you miss, the next time you may be mistaken. This article is inspired by "You do not know the volumes of JavaScript," a summary of this in Javasript.
The first step in learning this is to understand that this is neither a scope to point to the function itself nor to the function. This is actually the binding that occurs when a function is invoked, and where it points to depends entirely on where the function is invoked.
Default bindings
In JavaScript, the most common type of function invocation is a stand-alone function call, so you can think of this rule as a default rule when you cannot apply another rule. If the function is called without any modification, that is, a "bare" call, the default binding rule is applied, and the default binding is to the global scope.
function Saylocation () {
console.log (this.atwhere)
}
var atwhere = "I am in Global"
saylocation ()// Default bindings, this binds to global objects and outputs "I AM in Global"
Look at one more example
var name = "Global"
function person () {
console.log (this.name)//(1) "global"
person.name = ' inside '
function Sayname () {
console.log (this.name)//(2) "Global" is not "inside"
}
Sayname ()// Executes the Sayname function inside the person function, this points to the same global object
} person
()
In this case, the person function is invoked in the global scope, so this is bound to the global object (in the browser is window and global in node), so the sentence (1) Naturally outputs the Name property of a global object. Of course it's "global." The Sayname function is invoked within the person function, even if this is still a global object in the clause (2), even if the person function sets the Name property.
This is the default binding rule, which is the most common function invocation pattern in JavaScript, and the binding rule of this is the simplest of the four binding rules, which is binding on global scopes.
Strict mode in default bindings
In JavaScript, this cannot be bound to a global object if strict mode is used. The first example, but this time with the strict mode declaration
' Use strict '
function saylocation () {
console.log (this.atwhere)
}
var atwhere = "I am in Global"
Saylocation ()
//uncaught Typeerror:cannot Read Property ' Atwhere ' of undefined
As you can see, in strict mode, when you bind this to a global object, the undefined is actually bound, so the above code will give an error.
Implicit binding
When a function is called, the implicit binding rule binds this to the context object if the function has a so-called "foothold", that is, a context object. If you think the above statement is not straightforward enough, or look at the code.
function say () {
console.log (this.name)
}
var obj1 = {
name: "ZXT",
say:say
}
var obj2 = { C8/>name: "Zxt1",
Say:say
}
Obj1.say ()//ZXT
Obj2.say ()//ZXT1
It's simple, isn't it? In the above code, OBJ1, Obj2 is the end of the so-called say function, the professional point of view is the context object, when the function is assigned to this context object, the function inside this naturally points to this context object. This is also a common kind of function invocation pattern.
Implicit binding loss of context
function say () {
console.log (this.name)
}
var name = "Global"
var obj = {
name: "Inside",
Say:say
}
var alias = Obj.say//Set a shorthand (1)
alias ()//Function call output "global" (2)
Can see here the output is "global", why and the above example is not the same, we clearly just gave Obj.say changed a name it?
First we look at the code above (1), because in JavaScript, functions are objects, and objects are reference passes, not value passes. Therefore, the first (1) sentence code is only the alias = Obj.say = say, that is, alias = say, Obj.say just play a role in the bridge, the alias finally refers to the say function address, and the object of obj is irrelevant. This is the so-called "lost context". The final execution of the alias function simply executes the say function and outputs "global".
Explicit binding
Explicit binding, as the name suggests, shows this to bind to a context, JavaScript, which provides three ways to explicitly bind, Apply,call,bind. The use of apply and call is basically similar, and the difference between them is:
Apply (Obj,[arg1,arg2,arg3,...] The parameters of the called function are given in the form of an array
Call (Obj,arg1,arg2,arg3,...) The parameters of the called function are given in sequence
After the BIND function executes, a new function is returned. The following is a code description.
No parameter
function speak () {
console.log (this.name)
}
var name = "Global"
var obj1 = {
name: ' Obj1 '
}
var obj2 = {
name: ' Obj2 '
}
speak ()//Global is equivalent to Speak.call (window)
Speak.call ( window)
Speak.call (obj1)//Obj1
Speak.call (OBJ2)//Obj2
As you can see, apply, the function of call is to bind an execution context to a function, and it is explicitly bound. Therefore, this in the function is naturally bound to the object called by call or apply.
With parameter
function count (NUM1, num2) {
console.log (THIS.A * num1 + num2)
}
var obj1 = {
a:2
}< C23/>var obj2 = {
a:3
}
Count.call (Obj1, 1, 2)//4
count.apply (obj1, [1, 2])//4
count.call (ob J2, 1, 2)//5
count.apply (Obj2, [1, 2])//5
The above example illustrates the difference between apply and call usage.
The bind function, however, returns a new function that is bound to the specified execution context. Or take the above code as an example
With parameter
function count (NUM1, num2) {
console.log (THIS.A * num1 + num2)
}
var obj1 = {
a:2
}
VA R bound1 = Count.bind (obj1)//Unspecified Parameters
Bound1 (1, 2)//4
var bound2 = Count.bind (obj1, 1)//specified a parameter
bound2 (2) 4
var bound3 = Count.bind (obj1, 1, 2)//specified two parameters
Bound3 ()//4
var bound4 = Count.bind (obj1, 1, 2, 3)// Extra parameters are specified and extra parameters are ignored
bound4 ()//4
So the Bind method simply returns a new function, which specifies the execution context and returns the new function to accept the argument.
New binding
Finally, a this binding rule refers to the this binding that occurs when a constructor is invoked through the new operator. The first thing to be clear is that there are no concepts of classes in other languages in JavaScript. Constructors are just ordinary functions, except that the function name of the constructor begins with an uppercase letter, but it can be invoked by the new operator only.
function Person (name,age) {
this.name = name
This.age = Age
Console.log ("I'm just a normal function")
} person
( "ZXT", 22//"I'm just a normal function"
console.log (name)//"ZXT"
console.log (age)//
var zxt = new Person ("ZXT", 22)/ /"I'm just a normal function"
console.log (zxt.name)//"ZXT"
console.log (zxt.age)//22
In the example above, you first define a person function that can be invoked either normally or as a constructor. When the normal call is performed, the normal function executes, outputting a string. If you are using a new operator, you have constructed an object. So, let's take a look at the two invocation methods, which are bound to where the first normal call is, as described earlier, when the default binding rule is applied, this is bound to the global object, and the name and age two properties are incremented on the global object respectively. When invoked through the new operator, the function returns an object that is bound to the returned object from the output.
Therefore, the so-called new binding means that when a function is invoked via the new operator, a new object is generated and the this is bound to the object within the constructor.
In fact, in JavaScript, using new to invoke a function automatically performs the following actions.
- Create a new object
- This new object will be executed in a prototype connection.
- This new object is bound to this of the function call
- If the function does not return other objects, the function call in the new expression will automatically return the object
Precedence of four bindings
The four this binding rules in JavaScript are described above, and these four binding rules basically cover all function calls. But if two or more of these four rules were applied at the same time, what would be the case, or the order of precedence of the four bindings.
First, it is easy to understand that the default binding has the lowest precedence. This is because the default binding is invoked only if the other this binding rule cannot be applied. What about implicit binding and explicit binding? or code, the code can never lie.
function speak () {
console.log (this.name)
}
var obj1 = {
name: ' Obj1 ',
speak:speak
}
var Obj2 = {
name: ' Obj2 '
}
Obj1.speak ()//Obj1 (1)
Obj1.speak.call (OBJ2)//OBJ2 (2)
So in the code above, the Obj1.speak () is executed, and the speak inside the function points to obj1, so (1) The output of the code is OBJ1, of course, but when you explicitly bind this to speak within the OBJ2 function, the output becomes obj2, All from this result you can see that the precedence of an explicit binding is higher than implicit binding. In fact, we can understand this line of code Obj1.speak.call (OBJ2), obj1.speak only indirectly obtained the reference of the Speak function, which is a bit like the implicit binding lost context as previously mentioned. OK, now that the explicit binding is higher than the implicit binding, then compare the new binding to the explicit binding.
function foo (something) {
this.a = something
}
var obj1 = {}
var bar = foo.bind (obj1)//Return a new function bar, this new function This point to the Obj1 (1)
Bar (2)//This binding on the Obj1, so obj1.a = = 2
console.log (obj1.a)
var baz = new Bar (3)///After calling the new operator , this of the bar function points to the new instance returned Baz (2)
Console.log (obj1.a)
Console.log (BAZ.A)
We can see that at (1), this inside of the bar function points to obj1, but at (2), because of the new operator invocation, this inside the bar function points back to the returned instance, which indicates that the new binding has precedence over explicit binding.
At this point, the priority order of the four binding rules has been drawn, respectively
New binding > Explicit binding > Implicit binding > Default Binding
This binding in the arrow function
The arrow function is an important feature in ES6.
This of the arrow function is determined by the outer (function or global) scope. The This object in the body of the function refers to the object at which it was defined, not the object that was bound when the call was previously described. Give an example
var a = 1
var foo = () => {
console.log (THIS.A)//defined in global object, so this binding is in global scope
}
var obj = {
a:2< c19/>}
foo ()//1, calling
Foo.call (obj)//1 in the global object, showing bindings, invoked by the Obj object, without affecting the result
From the example above, this mandatory binding of the arrow function is scoped to the arrow function definition and cannot be modified by displaying bindings, such as the Apply,call method. Look at the following example
Defines a constructor function person
(name,age) {
this.name = name
This.age = age
this.speak = function () {
Console.log (this.name)
//Normal function (non-arrow function), this binding scope at call
this.bornyear = () => {
//This article was written in 2016, so new Date (). getFullYear () gets the 2016
//arrow function, this binding is console.log inside the instance
(new Date (). getFullYear ()-this.age)
}
}
}
var zxt = new Person ("ZXT")
zxt.speak ()//"ZXT"
zxt.bornyear ()//1994
//here should be all right.
var Xiaoming = {
name: "Xiaoming",
age:18//xiaoming is always 18 years old
}
zxt.speak.call (xiaoming)
//"Xiaoming" This is bound to xiaoming this object
zxt.bornYear.call (xiaoming)
//1994 instead of 1998, because this is always bound to be ZXT this instance
Therefore, the ES6 arrow function does not use four standard binding rules, but rather depends on the current lexical scope to determine this, specifically, the arrow function inherits the this binding of the outer function call, regardless of where the this binding of the outer function is.
Summary
This is the case for all of the This bindings in JavaScript, and before ES6, the four binding rules mentioned above can cover any function invocation, and after the ES6 standard is implemented, new arrow functions are added to the function extension, unlike before, The scope of the arrow function is the scope where the arrow function is defined.
For the previous four binding rules, mastering the invocation conditions of each rule is a good way to understand which scope this is bound to.