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 functionthis, and itPosition of writingClosely related, bound to its parent scope at the writing stagethis
  • constructionalthisWill bind to usnewOn this object that comes out
  • Without usingcall/apply/bindchangethisWhen 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 iswindow, sothisiswindow
  • 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 towindow
  • If the parent scope of the arrow function is the function scope, the arrow function points to the function scopethis
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’sthisIt 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 callcall/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