1. Introduction
This is one of the most important questions to ask in an interview. But many people have a vague understanding of this. They have to print it out at work before they dare to write it down. They can only give a general answer in an interview. Today we’re going to focus on that pain point and get rid of it!
Now let’s look at a problem:
var number = 5;
var obj = {
number: 3.fn: (function () {
var number;
this.number *= 2;
number = number * 2;
number = 3;
return function () {
var num = this.number;
this.number *= 2;
console.log(num);
number *= 3;
console.log(number); }}}) ()var myFun = obj.fn;
myFun.call(null);
obj.fn();
console.log(window.number);
Copy the code
The answers are, in order: 10, 9, 3, 27, 20. If you answered this correctly and easily, it means your understanding of this is very good. Otherwise, just read the passage with me
2. What is this?
When discussing this, it’s common to say “point to XXX.” This is a pointer, and before we get to the point, let’s introduce a few nouns:
- The default binding
- Implicit binding
- According to the binding
- The new binding
And their priority: New binding > Show binding > Implicit binding > Default binding.
2.1. Default binding
- Used when no other binding rules can be applied, usually stand-alone function calls.
- Independent function: a function in the global context whose this refers to:
- Non-strict and in a Node environment:
globalThis
; - Non-strict and Windows environment:
window
; - In strict mode:
undefined
.
- Non-strict and in a Node environment:
Unless otherwise stated, this article is the result of execution in the browser environment.
function getName(){
console.log('Name: '.this.name);
}
var name = 'laohuang';
getName();
Copy the code
When getName() is called, it is in the global context, the default binding is applied, this refers to the global object window, so the console prints: Name: laohuang.
2.2. Implicit binding
- A function is called by an object, or an object exists at the call location, i.e
obj.fn()
; - This refers to the last level in the chain of object properties. Such as
Obj1.obj2.obj3.fn (), this points to obj3
; - The implicit binding may be lost. Please remember:
obj.fn()
Is implicitly bound, but iffn()
There’s nothing up front. It’s the default binding.
2.2.1. Typicalobj.fn
function getName(){
console.log('Name: '.this.name);
}
var laohuang = {
name: 'laohuang'.getName: getName
}
var name = 'feifei';
laohuang.getName();
Copy the code
}}}}}}}}}}}}}}}}}}}}}}} GetName in LaoHuang is equivalent to an assignment operation. When calling laohuang.getName(), the location is laohuang. The implicit binding binds this in getName to laoHuang, so the console prints: Name: laohuang.
2.2.2. This refers to the last level in the chain of object properties
function getName(){
console.log('Name: '.this.name);
}
var feifei = {
name: 'feifei'.getName: getName
}
var laohuang = {
name: 'laohuang'.friend: feifei
}
var name = 'FEHuang';
laohuang.friend.getName();
Copy the code
Analytic: this point to the last layer in the chain of object properties, so the implicit binding to bind this to laohuang. The frend is feifei, so the console will print: Name: feifei.
2.2.3. The big trap of implicit binding – binding loss
1. Binding loss – When a reference to a function is to another variable:
function getName(){
console.log('Name: '.this.name);
}
var laohuang = {
name: 'laohuang'.getName: getName
}
var name = 'FEHuang';
var getNameCopy = laohuang.getName;
getNameCopy();
Copy the code
Var getNameCopy = laohuang. GetName = getName; getNameCopy = laohuang. GetName = getName; GetNameCopy () has nothing before it, so it’s the default binding, and this points to the global context. So the console will print: Name: FEHuang.
2. Binding loss – callback function:
function getName(){
console.log('Name: '.this.name);
}
var feifei = {
name: 'feifei'.getName: getName
}
var laohuang = {
name: 'laohuang'.getName: function() {
setTimeout(function() {
console.log('Name: '.this.name)
})
}
}
var name = 'FEHuang';
laohuang.getName(); // Name: FEHuang
setTimeout(feifei.getName, 1000); // Name: FEHuang
setTimeout(function() {
feifei.getName(); // Name: feifei
}, 1000)
Copy the code
Resolution:
laohuang.getName()
: That’s easy to understand.setTimeout
In the callback function ofthis
The default binding is used, and this time it is in non-strict mode, so printName: FEHuang
;setTimeout(feifei.getName, 1000)
: Here is equivalent tofeifei.getName
The quote is given directly tosetTimeout
The first variable, the last variable to execute. Here the binding is lost, the default binding is used, so point to the global context, print:Name: FEHuang
;setTimeout(function() { feifei.getName(); }, 1000).
: Although also insetTimeout
Callback, but here it is executed directlyfeifei.getName()
, using implicit binding,this
Point to thefeifei
. So print:Name: feifei
.
2.3. Show bindings
- Show binding is through
call
.apply
.bind
Is explicitly specifiedthis
The object to which the. call
.apply
andbind
The first argument to phi is phi of the corresponding functionthi
The object to which s points.call
andapply
The function of the same, but different ways of passing reference.call
andapply
Will execute the corresponding function, andbind
Method doesn’t.- Pay attention to
call
.apply
Pass null/undefined –> global context; Raw value –> Object (non-strict mode)/ raw value (strict mode)
2.3.1. Typical display bindings
function getName(){
console.log('Name: '.this.name);
}
var laohuang = {
name: 'laohuang'.getName: getName
}
var name = 'FEHuang';
var getNameCopy = laohuang.getName;
getNameCopy.call(laohuang);
Copy the code
The console will print: Name: laohuang.
2.3.2. Special Cases – Binding loss may also occur when using call, apply, or bind
So, does the use of a display binding mean that the loss of the binding encountered by an implicit binding does not occur? Clearly not so, do not believe, continue to read.
function getName(){
console.log('Name: '.this.name);
}
var laohuang = {
name: 'laohuang'.getName: getName
}
var name = 'FEHuang';
var getNameCopy = function(fn) {
fn();
};
getNameCopy.call(laohuang, laohuang.getName);
Copy the code
原 文 : GetNamecopy.call (laohuang, laohuang. GetName) does bind this to this. But the second argument to call passes a reference to getName, so when fn() is executed, it is equivalent to calling getName() directly. So the console will print: Name: FEHuang. Think: What if you want the binding not to be lost? (The answer is in the last thought 2)
2.3.3. Special cases – Special arguments for call, apply, bind
When using Call and Apply in non-strict mode, an attempt is made to convert to an object if the value used as this is not an object. Null and undefined are converted to global objects. Raw values such as 7 or ‘foo’ are converted to objects using the appropriate constructor.
1. Passing null/undefined: converts it to a global object, using the default binding.
var laohuang = {
name: 'laohuang'
}
var name = 'FEHuang';
function getName() {
console.log(this.name);
}
getName.call(null); //FeHuang
Copy the code
The default binding is applied, so the console will print: FEHuang.
2. Pass the original value: it will be converted to the corresponding object
var doSth = function(name){
console.log(this);
console.log(name);
}
doSth.call(2.'laohuang'); // Number{2}, 'laohuang'
var doSth2 = function(name){
'use strict';
console.log(this);
console.log(name);
}
doSth2.call(2.'laohuang'); // 2, 'laohuang'
Copy the code
2.4. The new binding
2.4.1. What did New do?
The introduction on MDN looks like this:
- Create an empty simple JavaScript object (that is, {});
- Link the object (set the object’s
constructor
) to another object; - Use the newly created object in Step 1 as
this
The context of; - Returns if the function returns no object
this
.
2.4.2. For example
function getName(name) {
this.name = name
}
var laohuang = new getName('laohuang');
console.log('Name: ', laohuang.name);
Copy the code
Var laohuang = new getName(‘laohuang’);} var laohuang = new getName(‘laohuang’);} So the console will print: Name: laohuang.
2.5. Arrow function
Let’s take a look at the characteristics of the arrow function:
- Functional in vivo
this
Object that inherits from the outer code blockthis
.Pay attention toThe “this” inside the: arrow function is not the object in which it was defined, but the “this” in the outer code block. - It cannot be used as a constructor, that is, it cannot use the new command, otherwise an error will be thrown.
- Do not use
arguments
Object that does not exist in the function body. You can use it if you wantrest
Parameter instead. - Do not use
yield
Command, so the arrow function cannot be usedGenerator
Function. - Arrow functions don’t have their own
this
, so it can’t be usedCall (), apply(), bind()
These are ways to changethis
Pointing to.
var names = {
getName0: function() {
console.log(this);
return () = > {
console.log(this); }},getName1: function() {
return function() {
console.log(this);
return () = > {
console.log(this); }}},getName2: () = > {
console.log(this); }}/ / the first paragraph
var name0 = names.getName0(); / / object names
name0(); / / object names
/ / the second paragraph
var name1 = names.getName1();
var _name1 = name1(); / / the window object
_name1(); / / the window object
/ / the third paragraph
names.getName2(); / / the window object
Copy the code
Resolution:
The first paragraph:
names.getName0()
Corresponding to implicit binding,this
Binding on thenames
So the console will print the: names object;name0()
It’s executing the arrow function. Arrow functionthis
Inheriting from a previous code segmentthis
(i.e.getName0()
The runtimethis
, i.e.,names
). So the console will print the: names object;
The second paragraph:
name1
isnames.getName1()
A completely new function is returned after running, which corresponds to the case of implicit binding loss described above. The default binding is applied,this
Points to theGlobal object Window
. soname1()
Print: window object;_name1()
It executes the arrow function.- If the arrow function this inherits from the custom object, then
_name1()
The name object should be printed, but here the Window object is printed, so obviously the understanding iserror. - In accordance with the
The this of the arrow function is inherited from the this of the outer code block
It makes sense. The outer code block that we just analyzed,this
Points to thewindow
, so here the console prints the: window object.
- If the arrow function this inherits from the custom object, then
The third paragraph:
- Names.getname2 () executes the arrow function. Because this does not exist in the current code block names, you can only search at the upper level. So here the console prints the: window object.
2.5.1. Remember that this in the arrow function inherits from this in the outer code library block
It is also possible that this in the arrow function is dynamic.
var names = {
getName1: function() {
return function() {
console.log(this);
return () = > {
console.log(this); }}}},var name0 = names.getName1();
var n1 = name0(); // window
n1(); // window
var n2 = name0.call(names); // names
n2(); // names
Copy the code
3. Summary
3.1. How to accurately judge the direction of this
As a refresher, the priority for bindings is: New binding > Show binding > Implicit binding > Default binding. Then we can judge by the following steps:
- Whether the function is called in new (new binding), if so, then this is bound to the newly created object;
- Whether the function is called via call,apply, or bind. If so, then this binds to the specified object.
- Whether the function is called in a context object (implicit binding), if so, this is bound to that context object. It’s usually obj.fun();
- If none of the above, use the default binding. If in strict mode, it is bound to undefined, otherwise to a global object (globalThis for the Node environment, window for the browser environment);
- If null or undefined is passed to call, apply, or bind as a binding object to this, these values are ignored. The default binding rules apply.
- In the case of an arrow function, the this of the arrow function inherits from the this of the outer code block.
Finally, this point needs more practice, this article is just a list of the general, only continuous practice to master oh ~ error, welcome to correct oh ~
4. Think about
4.1. Thinking about 1
var number = 5;
var obj = {
number: 3.fn: (function () {
var number;
this.number *= 2;
number = number * 2;
number = 3;
return function () {
var num = this.number;
this.number *= 2;
console.log(num);
number *= 3;
console.log(number); }}}) ()var myFun = obj.fn;
myFun.call(null);
obj.fn();
console.log(window.number);
Copy the code
Resolution:
- Definition phase: Fn’s corresponding closure is executed when obj is defined. When you execute the code in the closure and return the function, it is clear that this is not a new binding (no new keyword), it is not a display binding (no call, aply, bind), it is not an implicit binding (no obj.fun()), so it is the default binding. This refers to the global variable window. The data change process is as follows:
var number; // The newly declared number. Because it is a closure, the number will not be destroyed.
this.number *= 2; Window. number: 5 * 2 --> 10
number = number * 2; // There is no call through this, where number is the new one declared in the closure. Closure number: undefined --> NAN.
number = 3; // closure number: NAM --> 3.
Copy the code
- Var myFun = obj.fn: assign a reference to obj.fn to myFun. The implicit binding is clearly missing here, so it should be the default binding. MyFun’s this will point to the global variable window. It’s just assignment, there’s no method call, so the data doesn’t change.
- Myfun.call (NULL) : Call is used, but the first argument is NULL, so the default binding is still applied. The data changes as follows:
// myFunc
function () {
var num = this.number; // this points to window. So num === window.number, value 10.
this.number *= 2; // this points to window. Window. number: 10 * 2 --> 20
console.log(num); // print 10 if num is 10.
number *= 3; // Not called through this, where number is the one in the closure. Closure number: 3 * 3 --> 9.
console.log(number); // Print the closure number, print 9.
}
Copy the code
- Obj.fn () : the typical default binding, where this refers to obj. Therefore, the data changes as follows:
// obj.fn
function () {
var num = this.number; // this points to obj. So num === obj.number, value 3.
this.number *= 2; // this points to obj. Obj. Number: 3 * 2 --> 6.
console.log(num); // if num is 3, print 3
number *= 3; // Not called through this, where number is the one in the closure. Closure number: 9 * 3 --> 27.
console.log(number); // Print the closure number, print 27
}
Copy the code
- Console. log(window.number) : In this case, window.number is 20.
- So, the final print is: 10, 9, 3, 27, 20.
4.2. Thinking about 2
Problem: Show bindings – what do I do if I want bindings not to be lost? Answer: When calling fn, give it a display binding as well.
function getName(){
console.log('Name: '.this.name);
}
var laohuang = {
name: 'laohuang'.getName: getName
}
var name = 'FEHuang';
var getNameCopy = function(fn) {
fn().bind(this);
};
getNameCopy.call(laohuang, laohuang.getName);
Copy the code
Reference 5.
- Hey, do you really understand this?
- MDN – this
- Interviewer: JS this point