Deep understanding of this binding principle

Keywords: Javascript github

Preface

A while ago, I watched the comprehensive parsing of this in JavaScript you don't know. It was very good to talk about. My little buddies who haven't yet seen it grabbed the time to learn. Here I deliberately sorted out and summarized a wave to share the learning.Before that, you can understand About this, call, applay, and bind This article.

Call Stack

Before you get to know how this binding works, the first page starts with an understanding of the JavaScript execution stack.(Execution stack is also called call stack) If you don't know the call stack yet, you can learn more about it first Dig into the JavaScript execution context and execution stack.

JavaScriptIs single-threaded, all this determines that only one thing can be done at the same time, other activities or things can only be queued, and then a queued execution stack is generated( Execution Stack). Simply put, the execution stack is all the functions that are called to reach the current execution location.The call location is in the previous call to the function currently executing.

function baz() {
    // The current execution stack is:baz
    // The call location is in the previous call to baz and is therefore Global
    console.log('baz');
    bar();
}

function bar() {
    // The current execution stack is:bar
    // The call location is in the previous call to bar, so it is baz
    onsole.log('bar');
}

baz()  // Call location of baz

Based on the rules above, it is not difficult to determine the call stack and location of the above code.The call stack and location determine the binding object for this.There are four binding rules, and it is only when the call location is found that one of the four rules needs to be applied.

Default Binding

In non-strict mode, this points to a global object, while in strict mode, this points to undefined

function foo() {
  console.log(this.a); // this points to global objects
}
var a = 2;
foo(); // 2
function foo2() {
  "use strict"; // Strict mode this bound to undefined
  console.log(this.a); 
}
foo2(); // TypeError:a undefined

However, if other functions are called in strict mode, the default binding is not affected.

function foo() {
  console.log(this.a); // foo function is not strictly mode default binding global object
}
var a = 2;
function foo2(){
  "use strict";
  foo(); // Call other functions in strict mode without affecting default binding
}
foo2();

The above is a stand-alone function call with a global positioning and all this bound to the global scope.

Implicit Binding

The function at the call location, whether there is a context object, and if so, this is implicitly bound to this object.
It can also be simply understood as whether or not it is contained by an object.

function foo() {
  console.log(this.a);
}
var a = "Oops, global";
let obj2 = {
  a: 2,
  foo: foo
};
let obj1 = {
  a: 22,
  obj2: obj2
};
obj2.foo(); // 2 this points to the object calling the function
obj1.obj2.foo(); // 2 this Points to the object calling the function at the last level

// Implicit binding missing
let bar = obj2.foo; // bar is just a function alias and a reference to obj2.foo
bar(); // "Oops, global" - Point to Global

In the above code, the function foo call location is in the obj2 context, all this are bound in the obj2 scope, so obj2.foo(), obj1.obj2.foo() are all eventually 2, while let bar = obj2.foo is actually a reference to the function assigned to the variable bar. When called, there is no context object, so the implicit binding is lost.

Explicit Binding

Force this in the function to bind to the specified object by apply, call, bind.

function foo() {
    console.log(this.a);
}
let obj = {
    a: 2
};
foo.call(obj); // 2

Note that:

  • If an original value (string, Boolean type, numeric type) is passed in as the binding object for this, the original value will be converted to its object form.
  • If null or undefined is passed into call, apply, bind as the binding object for this, these values will be ignored during the call. The actual application is the default binding rule.

new Binding

If you don't know the new keyword, read this first About the new command.

When using a construct call, this automatically binds to objects created during new.

function foo(a) {
  this.a = a; // this bound to bar
}
let bar = new foo(2);
console.log(bar.a); // 2

Priority of four binding rules

  • Explicit binding > Implicit binding > Default binding
  • new binding > implicit binding > default binding
function foo() {
    this.a = 100;
}
var obj1 ={
    foo: foo;
}
var obj2 = {}
obj1.foo.call(obj2, 2); // The 2 this-to-obj2 explicit binding takes precedence over the implicit binding.

new obj1.foo(4); // thsi has a higher priority for new binding to newly created objects than implicit binding.

Explicit bindings and new bindings cannot be compared directly (with errors), and default bindings have the lowest priority because they should not be the back-end bindings after other rules.

The arrow function this points to

The arrow function this does not use these four binding rules.

function foo() {
  return (a) => {
    // this inherits from foo
    console.log(this.a);
  };
}
let obj1 = {
  a: 2
};
let obj2 = {
  a: 3
};
let bar = foo.call(obj1); // foo this points to obj1
bar.call(obj2); // Output 2 Execute arrow function here and try to bind this to point to obj2

From the above, the this rule for the arrow function is:

  • The this in an arrow function inherits the this direction of the first function outside it that is not an arrow function.If not, point to global.
  • Once the context is bound to the this of the arrow function, no code changes.

Summary

If you want to determine a running function this binding, you need to find the location where the function is called directly.Once found, the following four rules can be applied sequentially to read this bound object.

  1. There is a new call, bound to the newly created object.
  2. Called by call or apply or bind to the specified object.
  3. Called by a context object and bound to that context object.
  4. Default: bound to undefined in strict mode, otherwise bound to global

The arrow does not apply to the four rules above, but is inherited from its outer function.

More quality articles are available GitHub Blog, welcome to Star!!!

Posted by arbelo on Sun, 08 Dec 2019 08:40:03 -0800