Cognition about this

What is this

This is an object that is automatically generated inside the function body at runtime and can only be used inside the function body. The value of this varies from function to function. In general, this is the environment object in which the function is run.

This points to the

This refers neither to the function itself nor to the lexical scope of the function.

This binding

This is bound at run time, not at write time, and its binding has nothing to do with where the function is declared, except how the function is called. Also, the context of this depends on various conditions when the function is called.

The position

Where the function is called determines the binding object for this. Therefore, before we can understand the binding rules for this, we need to understand where the function is called (not declared): in the previous call to the currently executing function.

To figure out where to call, you need to analyze the call stack. Let’s look at a simple example:

// This example is from the section called Location in JavaScript you Don't know
function baz() {
  // Current call stack: baz
  // Current calling location: global scope
  console.log("Call stack: baz");
  bar(); // <-- bar call location
}

function bar() {
  // Current call stack: baz -> bar
  // Current call location: baz
  console.log("Call stack: bar");
  foo(); // <-- foo's call location
}

function foo() {
  // Current call stack: baz -> bar -> foo
  // Current call location: bar
  console.log( "Call stack: foo" );
}

baz(); // <-- baz call location
Copy the code

The following figure shows the Call Stack during execution of the above code


figure 1.1 The call stack Figure 1.1 – Call stack

By reading the above picture and text, you must have an understanding of the call stack and call location. Let’s take a look at the binding rules for this.

This binding rule

There are four binding rules for this:

  • The default binding
  • Implicit binding
  • Explicitly bound
  • The new binding

The binding of this occurs when the function is called, and what it points to depends entirely on where the function is called. When analyzing which binding rules to use, be sure to specify the function call location. Let’s take a look at these four rules.

The default binding

Case code

// The global variable a defined in the global scope becomes an attribute of the same name of the global object
var a = 2; 
function foo() {
    console.log('this.a--->'.this.a);
    console.log('this-->'.this);
}

foo();
Copy the code

The results show

The above type of function call, often called a stand-alone function call (also called a pure function call), is the most common use of a function and is a global call. It uses the default binding for this, so this points to a global object in the function (note that the default binding cannot be used in strict mode; this points to undefined).

How do we assume that foo() applies the default binding? Notice where it is called. In the code above, foo() is called directly with an undecorated function reference; it can’t apply any other rules, so it uses the default binding.

Using default binding rules often means that no other rules can be applied. So, we can think of this rule as the default rule when no other rule can be applied.

Implicit binding

When a function reference has a context object, the implicit binding rule binds this in the function call to that context object. Therefore, when determining whether an implicit binding rule applies to a function being called, we look at whether there is a context object where it is called.

Case code

let obj = {
    num: 2.getNum: function () {
      console.log('this.num--->'.this.num);
      console.log('this--->'.this); }}; obj.getNum();Copy the code

The results show

Implicit binding considerations:

  1. Only the top or last level in the object attribute reference chain affects the call location. If you look at this example,This in the function will point to obj2, not obj1.

Case code

let obj2 = {
    num: 10.getNum: function () {
      console.log('this.num--->'.this.num);
      console.log('this--->'.this); }};let obj1 = {
    num: 1.obj2: obj2 
};

obj1.obj2.getNum();
Copy the code

* Results display

  1. Implicit loss

The problem is that implicitly bound functions lose bound objects. In other words, it applies the default binding. Note that the default binding binds this to a global object or undefined (depending on whether strict mode is enabled in the code).

Case Code 1

function getNum() {
  console.log("this.a--->".this.a);
  console.log("this--->".this);
}

var a = "hello world"; // a is an attribute of the global object
var obj = {
    a: 2.getNum: getNum
};

var alias = obj.getNum;

alias();
Copy the code

Alias in this code is a reference to obj.getNum, but it refers to the getNum function itself. Therefore, when you call alias(), you are actually calling a function without any decorations, so the default binding rules apply.

* Results display

Case Code 2

function getNum() {
    console.log("this.a--->".this.a);
    console.log("this--->".this);
}

function done(fn) {
    // fn actually refers to getNum
    fn(); // <-- call location
}

var a = "Another implicit loss."; // a is an attribute of the global object
var obj = {
    a: 2.getNum: getNum
};

done(obj.getNum); // "another implicit loss"
Copy the code

Parameter passing is essentially an implicit assignment, not special just because the argument passed is a function. So this case is not that different from the last one. Passing functions as arguments (that is, callback functions) is quite common, but we have no control over how callback functions are executed, and therefore no control over where they are called that affects the binding, which can cause implicit loss problems if we are not careful.

* Results display

In addition, passing our declared function into the language’s built-in function (for example, setTimeout) gives the same result, no difference. In this case, I’ll leave it to you to debug.

Explicitly bound

Call and apply

Call () and apply() should be familiar to you. They are methods on functions, and they are used by most JavaScript functions and the ones we create ourselves. Both methods take an object as their first argument, which they bind to this, which they then specify when calling the function. We call this an explicit binding because we can specify the binding object for this directly.

Case code

function getNum() {
    console.log("this.a--->".this.a);
    console.log("this--->".this);
}

var a = 'Show binding to -- global object';
var obj = {
    a: "Show binding to -- specified object"};// Call () is the same as apply() from the perspective of this binding, so only call is used here for testing
getNum.call(obj);
console.log("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --");
getNum.call();
Copy the code

Note that global objects are called by default when call() or apply() has a null (or null) argument. In addition, if you pass them a raw value as a binding object to this, the original value will be converted to its object form (for example, new Number()). This is often referred to as “boxing”.

* Results display

Hard binding

A hard binding is an explicit forced binding. It solves the problem of missing bindings that explicit binding cannot solve.

Case code

function getNum() {
    console.log("this.a--->".this.a);
    console.log("this--->".this);
}

function handle() {
    getNum.call(obj);
}

var obj = { a: 2 };

handle();

console.log("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -");

handle.call(window); // The hardbound Handle does not modify its this

console.log("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -");

setTimeout(handle, 100);
Copy the code

Because getnum.call (obj) is manually called inside the handle() function, we force getNum’s this to be bound to the obj object. Then no matter how you call handle, it will call getNum on obj.

* Results display

As you may have noticed from the simple hard binding example given above, hard binding is wrapped inside a function. In fact, a typical use of hard binding is to create a wrapping function.

function getNum() {
    console.log(this.a);
}

// an auxiliary binding function
function bind(fn, obj) {
    return function () {
      return fn.apply(obj, arguments);
    };
}

var obj = { a: 2 };
var handle = bind(getNum, obj);

handle(); / / 2
Copy the code

In fact, there is a built-in hard-bound method called function.prototype.bind available in ES5. The bind method returns a hard-coded new function, and it sets the argument to the context of this and calls the original function.

Case code

function getNum() {
    console.log("this.a--->".this.a);
    console.log("this--->".this);
}

var obj = { a: 2 };
var fn = getNum.bind(obj);

fn(); / / 2
console.log(Return a hard-coded function -->, getNum.bind);
console.log("fn--->", fn);
Copy the code

* Results display

The new binding

In JavaScript, when we call a function with new, the new binding rule is applied. Functions called by the new operator are called constructors (there are no constructors, only “constructor calls” to functions).

When a function is called using new, the following action is automatically performed (see JavaScript you Don’t Know, vol. 1) :

  1. Create (or construct) a brand new object.
  2. The new object will be connected by the implementation [[prototype]].
  3. This new object is bound to the function call’s this.
  4. If the function returns no other object, the function call in the new expression automatically returns the new object.

Case code

function Foo(a) { 
    this.a = a;
}

var f = new Foo(2); 
console.log( f.a ); / / 2
Copy the code

* Results display

In this example code, when we call Foo() with the new operator, we construct a new object and bind it to this in the Foo() call.

Main Reference Sources

  1. JavaScript you Don’t Know, vol. 1

  2. Javascript this usage — Ruan Yifeng