JS this, always god god, accidentally wrong.
I hope that after I finish writing this article, I will also analyze this according to the current streamlined logic.
TL; DR
- Arrow function
this
, and itPosition of writingClosely related, bound to its parent scope at the writing stagethis
- constructional
this
Will bind to usnew
On this object that comes out - Without using
call/apply/bind
changethis
When I point to the normal functionthis
.onlyWhen called, bind toThe callerHas nothing to do with its location- Execute functions immediately, setTimeout, setInterval internally yesCommon functionBecause its caller is
window
, sothis
iswindow
- Execute functions immediately, setTimeout, setInterval internally yesCommon functionBecause its caller is
- Call /apply/bind can change the this direction, and can be done by hand
This in the arrow function
The arrow function’s this, which is closely related to the position at which it is written, is bound to its parent’s this at the writing stage (the declaration position).
Because it is determined by the parent scope, the parent scope is crucial.
- If the parent scope of the arrow function is global, this always points to
window
- If the parent scope of the arrow function is the function scope, the arrow function points to the function scope
this
var a = 1;
var obj = {
a: 2.func2: () = > {
// The parent scope is global. This is always window
console.log(this.a);
},
func3: function() {
// equivalent to func3 scoped this() = > {console.log(this.a); }; }};// func1
var func1 = () = > {
// The parent scope is global. This is always window
console.log(this.a);
};
// func2
var func2 = obj.func2;
// func3
var func3 = obj.func3;
func1();
func2();
func3();
obj.func2();
obj.func3();
Copy the code
Constructor this
The this in the constructor will bind to our new object:
function Person(name) {
this.name = name;
console.log(this);
}
/ / this is the person
var person = new Person("yan");
Copy the code
Function A normal function defined by function
For ordinary functions defined by function, the orientation of this is determined at call time, not at write time. This is the opposite of closures.
In other words: no matter where the method is written, its this will simply follow its caller, and in plain English xx.fn(), this in fn is whoever is in front of the dot. No point is window.
Note the premise above: do not change this reference using call/apply/bind.
A strict distinction must be made between “declare location” and “call location”!!
With the above tips in hand, the following example is easy
// Declare the location
var me = {
name: "yan".hello: function() {
console.log('Hello, I amThe ${this.name}`); }};var you = {
name: "xiaoming".hello: function() {
vartargetFunc = me.hello; targetFunc(); }};var name = "BigBear";
// Call location
you.hello();
Copy the code
There are also two cases of special points: there is no caller, so this is always window
- Execute functions now (IIFE),
(function(){})()
- The normal function passed in setTimeout/setInterval,
setTimeout(function(){... }, 1000).
!!!!!!!!! Note that it must be a normal function written by function, if the arrow function, still follow the arrow function rules
Let’s look at an example of a function that executes immediately,
var name = "BigBear";
var obj = {
name: "yan".fn: function() {(function() {
console.log(this.name);
})();
}
};
obj.fn();
Copy the code
If you look carefully, this is a self-executing function. This must be a window.
Attention!! With the arrow function, the rule changes, because the parent scope is fn, and its this is bound to obj, so it prints yan
var obj = {
fn: function() {(() = > {
console.log(this);
})();
}
};
obj.fn();
Copy the code
See the setTimeout
var name = "BigBear";
var me = {
name: "yan".hello: function() {
setTimeout(function() {
console.log('Hello, I amThe ${this.name}`); }); }}; me.hello();Copy the code
The function inside setTimeout, this points to the window, and the natural value is BigBear. Similarly, if you change the setTimeout function to an arrow function, the yan is printed.
var name = "BigBear";
var me = {
name: "yan".hello: function() {
setTimeout(() = > {
console.log('Hello, I amThe ${this.name}`); }); }}; me.hello();Copy the code
This in strict mode
The author need not strict mode, really want to use when baidu next bar.
Change the direction of this
The reference to this is either restricted by the writing position or the calling position, so it is passive.
- For the arrow function, because it’s
this
It only depends on the writing position, so you generally don’t change the arrow function’s this pointer. - Constructor. This is an instance of new, so the constructor’s this pointer is not normally modified
- So the point!! For ordinary functions defined by function, to modify this, you must display the call
call/apply/bind
.
This is a normal function defined by function.
Call /apply/bind
- Call /apply changes the this of the function, and the function executes immediately. But apply takes an array and call takes a non-array
- Bind changes this of the function, but the function is not executed
var init = 0;
function add(num1, num2) {
console.log(this.init + num1 + num2);
}
// Normal execution, positive output 3
add(1.2);
var obj = { init: 100 };
// This becomes obj, so output 103
add.apply(obj, [1.2]);
// This becomes obj, so output 103
add.call(obj, 1.2);
// This becomes obj, but you need to call the function once. Bind itself returns the function
add.bind(obj, 1) (2);
Copy the code
Handwriting implementation of call/apply/bind
In fact, a closer look at call shows that it has the following characteristics:
- Call is a function method that can be called directly by a function
- The first argument to call is the object bound to this, followed by arguments to the function
- After the call is called, the function executes, but this is bound to the first argument
How to implement call:
- Prototype is a method of a Function, each Function can be called, can be written on function.prototype
- Parameters distinguish the first parameter from the following parameters
- In call, this is bound to the first argument, and the function executes
Fn () => obj (fn) => Obj (fn) => obj (fn) => obj (fn)
Function.prototype.myCall = function(context, ... args) {
if (context === null) {
returnfn(... args); }// Notice that this is the function that calls call
const fn = this;
/ / add first
context.fn = fn;
/ / execution
returnfn(... args);/ / delete
delete context.fn;
};
// Test it, no problem, output 103
add.myCall(obj, 1.2);
Copy the code
In the same way:
// Note that since the args argument is an array, do not add...
Function.prototype.myApply = function(context, args) {
if (context === null) {
returnfn(... args); }Note that this is the function that calls myApply
const fn = this;
context.fn = fn;
returnfn(... args);delete context.fn;
};
// Do the same with 103
add.myApply(obj, [1.2]);
Copy the code
Bind is slightly more complicated:
- Bind is a function method that can be called directly by the function
- Bind returns a copy of the original function, but this is specified
- Bind can also pass some arguments to a function
Bind returns a function that calls call~
Function.prototype.myBind = function(context, ... frontArgs) {
const fn = this;
return function(. behindArgs) {
returnfn.call(context, ... frontArgs, ... behindArgs); }; };// This is specified, but needs to be called once to execute the function, printing 103
add.myBind(obj, 1) (2);
Copy the code