This can be said to be one of the most intriguing features in javascript, just like the tenses in high school English, such as the passive tense, the past tense, the present tense, and the past tense, no matter how many times you miss it, the next error may still occur. This article is inspired by the JavaScript volume you don't know, and summarizes this in javasript. This can be said to be one of the most intriguing features in javascript, just like the tenses in high school English, such as the passive tense, the past tense, the present tense, and the past tense, no matter how many times you miss it, the next error may still occur. This article is inspired by the JavaScript volume you don't know, and summarizes this in javasript.
The first step of learning this is to understand that this is neither pointing to the function itself nor to the function scope. This is actually a binding that occurs when a function is called. The point to which it points depends entirely on where the function is called.
Default binding
In javascript, the most common function call type is independent function call. Therefore, this rule can be considered as the default rule when other rules cannot be applied. If a function is called without any modification, that is, a "Bare" call, the default binding rule is applied. The default binding rule is global.
Function sayLocation () {console. log (this. atWhere)} var atWhere = "I am in global" sayLocation () // bound by default. this is bound to a global object and "I am in global" is output"
Let's look at another 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 () // execute the sayName function within the person function. this also points to the global Object} person ()
In this example, the person function is called in the global scope, so this in the (1) sentence is bound to the Global Object (in the browser, It is a window, in node, it is global, so the (1) Sentence naturally outputs the name attribute of a global object, of course, it is "global. The sayName function is called in the person function, even if this in the (2) sentence is still a global object, even if the person function sets the name attribute.
This is the default binding rule, which is the most common function call mode in javascript. this binding rule is also the simplest of the four binding rules, that is, binding to the global scope.
The strict mode in the default binding
In javascript, if the strict mode is used, this cannot be bound to a global object. Let's take the first example, but this time we added a strict pattern statement.
'use strict' function sayLocation() { console.log(this.atWhere) } var atWhere = "I am in global" sayLocation() // Uncaught TypeError: Cannot read property 'atWhere' of undefined
It can be seen that in strict mode, when this is bound to a global object, it is actually bound to undefined, so the above Code will report an error.
Implicit binding
When a function is called, if the function has a so-called "foothold" and a context object, the implicit binding rule binds this in the function to this context object. If you think the above section is not straightforward enough, let's look at the code.
function say() { console.log(this.name) } var obj1 = { name: "zxt", say: say } var obj2 = { name: "zxt1", say: say } obj1.say() // zxt obj2.say() // zxt
Is it easy. In the above Code, obj1 and obj2 are the foothold of the so-called say function. Specifically, they are context objects. When this context object is specified for a function, this inside the function naturally points to this context object. This is also a common function call mode.
Context loss during implicit binding
Function say () {console. log (this. name)} var name = "global" var obj = {name: "inside", say: say} var alias = obj. say // set a abbreviation (1) alias () // function call output "global" (2)
We can see that the output here is "global". Why is it different from the previous example? We just changed the name for obj. say?
First, let's look at the code in the (1) Sentence above. Because in javascript, functions are objects, and objects are passed by reference, rather than values. Therefore, the code for the (1) sentence is alias = obj. say = say, that is, alias = say, obj. "say" only serves as a bridge. alias eventually references the address of the "say" function, but it has nothing to do with the object "obj. This is the so-called "Lost context". Finally, the alias function is executed, but the say function is simply executed to output "global ".
Explicit binding
Explicit binding, as the name suggests, explicitly binds this to a context. javascript provides three explicit binding methods: apply, call, and bind. The usage of apply and call is basically similar. The difference between them is:
Apply (obj, [arg1, arg2, arg3,...] parameters of the called function are given in an array.
Call (obj, arg1, arg2, arg3,...) parameters of the called function are given in sequence.
After the bind function is executed, a new function is returned. The following code is used to describe.
// Function speak () {console. log (this. name)} var name = "global" var obj1 = {name: 'obj1'} var obj2 = {name: 'obj1'} speak () // global is equivalent to speak. call (window) speak. call (window) speak. call (obj1) // obj1 speak. call (obj2) // obj2
Therefore, we can see that the application and call function is bound to an execution context explicitly. Therefore, this in the function is naturally bound to the objects called by call or apply.
// Function count (num1, num2) {console. log (this. a * num1 + num2)} var obj1 = {a: 2} var obj2 = {a: 3} count. call (obj1, 1, 2) // 4 count. apply (obj1, [1, 2]) // 4 count. call (obj2, 1, 2) // 5 count. apply (obj2, [1, 2]) // 5
The above example illustrates the difference between the use of apply and call.
The bind function returns a new function bound to the specified execution context. Take the above Code as an example.
// Function count (num1, num2) {console. log (this. a * num1 + num2)} var obj1 = {a: 2} var bound1 = count. bind (obj1) // The parameter bound1 (1, 2) is not specified // 4 var bound2 = count. bind (obj1, 1) // specifies a parameter bound2 (2) // 4 var bound3 = count. bind (obj1, 1, 2) // specify two parameters: bound3 () // 4 var bound4 = count. bind (obj1, 1, 2, 3) // The excess parameters are specified. The excess parameters are ignored. bound4 () // 4
Therefore, the bind method only returns a new function. this in this function specifies the execution context, and the new function returns acceptable parameters.
New binding
The last this binding rule refers to the this binding that occurs when the constructor is called through the new operator. The first thing to be clear is that javascript does not have classes in other languages. A constructor is just a common function. It only starts with an uppercase letter and can be called using the new operator.
Function Person (name, age) {this. name = name this. age = age console. log ("I am just a common function")} Person ("zxt", 22) // "I am just a common function" console. log (name) // "zxt" console. log (age) // 22 var zxt = new Person ("zxt", 22) // "I am just a common function" console. log (zxt. name) // "zxt" console. log (zxt. age) // 22
In the preceding example, a Person function is defined, which can be called in both common and constructor forms. When a common call is used, a string is output according to the normal function execution. If a new operator is used, a new object is constructed. Then, let's take a look at the two call Methods: where is this bound? First, when it is called normally, we have already introduced it. At this time, the default binding rule is applied, this is bound to a global object. The name and age attributes are added to the global object. When called using the new operator, the function returns an object. From the output result, this object is bound to the returned object.
Therefore, the so-called new binding means that when a function is called through the new operator, a new object will be generated and this in the constructor will be bound to this object.
In fact, in javascript, using new to call a function automatically performs the following operations.
1. Create a new object
2. The new object will be connected by a prototype.
3. this new object will be bound to the function call's this
4. If the function does not return other objects, function calls in the new expression will automatically return this new object.
Four binding priorities
The above describes four this binding rules in javascript. These four binding rules basically cover all function calls. But if two or more of the four rules are applied at the same time, what is the case, or what is the priority order of the four bindings.
First, it is easy to understand that the default binding priority is the lowest. This is because the default binding is called only when other this binding rules cannot be applied. What about Implicit binding and explicit binding? Let's move on to the Code. The Code 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 execution of obj1.speak (), this inside the speak function points to obj1, so (1) the code output is of course obj1, however, when this is explicitly bound to obj2 in the speak function, the output result becomes obj2. From this result, we can see that the priority of explicit binding is higher than that of implicit binding. In fact, we can understand this line of code obj1.speak. call (obj2). obj1.speak only indirectly obtains the reference of the speak function, which is a bit like the implicit binding mentioned earlier, which loses the context. Well, since the explicit binding takes a higher priority than the implicit binding, next we will compare the new binding with the explicit binding.
Function foo (something) {this. a = something} var obj1 = {} var bar = foo. bind (obj1) // return a new function bar. this in the new function points to obj1 (1) bar (2) // this is bound to obj1, so obj1.a === 2 console. log (obj1.a) var baz = new bar (3) // after the new operator is called, this of the bar function points to the returned new instance baz (2) console. log (obj1.a) console. log (baz. a)
We can see that at (1), this inside the bar function originally points to obj1, but at (2), because it is called by the new operator, this inside the bar function directs to the returned instance again, which indicates that the priority of the new binding is higher than that of the explicit binding.
So far, the priority sorting of the four binding rules has been obtained, respectively
New binding> explicit binding> implicit binding> default binding
This binding in the arrow Function
Arrow functions are an important feature in es6.
This of Arrow functions is determined by the outer (function or global) scope. This object in the function body refers to the object in the definition, rather than the object bound in the call described earlier. For example
Var a = 1 var foo = () => {console. log (this. a) // is defined in the Global object, so this is bound to the global scope} var obj = {a: 2} foo () // 1, and foo is called in the global object. call (obj) // 1. The binding is displayed and called by the obj object, but the result is not affected.
As shown in the preceding example, this of the arrow function is forcibly bound to the scope of the arrow function definition, and cannot be modified by displaying the binding, such as apply and call methods. Let's take a look at the example below.
// Define a constructor function Person (name, age) {this. name = name this. age = age this. speak = function () {console. log (this. name) // common function (non-arrow function), this is bound to the scope of the call} this. bornYear = () => {// This article is written on July 15, 2016, so new Date (). getFullYear () returns the 2016 // arrow function. this is bound to the internal console of the instance. log (new Date (). getFullYear ()-this. age) }}var zxt = new Person ("zxt", 22) zxt. speak () // "zxt" zxt. bornYear () // 1994 // there should be no problem with this. var xiaoMing = {name: "xiaoming", age: 18 // James is always 18 years old} zxt. speak. call (xiaoMing) // "xiaoming" this is bound to the xiaoMing object zxt. bornYear. call (xiaoMing) // 1994 instead of 1998, because this is always bound to the zxt instance
Therefore, the arrow function of ES6 does not use four standard binding rules, but determines this based on the current lexical scope. Specifically, the arrow function inherits the this binding called by the outer function, no matter where the outer function is bound with this.
Summary
The above is the binding of all this in javascript. Before es6, the four binding rules mentioned above can cover any function call conditions. After es6 standard implementation, the arrow function is added for Function Extension. different from the previous one, the arrow function scope is located in the scope of the arrow function definition.
For the previous four binding rules, understanding the call conditions of each rule can give a good understanding of the scope in which this is bound.