Wrong idea about this

This refers to the function itself

This misconception may be due to the fact that in JavaScript, since a function is an object, it is possible to store some values as keys/values on that function object, as in the following example:

function addCount() {
  this.count++;
  console.log('the count result:'.this.count)
  console.log('addCount. Count:, addCount.count)
}

addCount.count = 0;

for (let i = 0; i < 3; i++) {
  console.log(` = = = = = = = = = = = = = = =${i+1}= = = = = = = = = = = = = = = `)
  addCount(i)
}

// Here is the output:= = = = = = = = = = = = = = =1= = = = = = = = = = = = = = = the count result:NaNAddCount. Count:0= = = = = = = = = = = = = = =2= = = = = = = = = = = = = = = the count result:NaNAddCount. Count:0= = = = = = = = = = = = = = =3= = = = = = = = = = = = = = = the count result:NaNAddCount. Count:0
Copy the code

When addcount.count = 0, we do add a count attribute to the function object addCount. However, this in this. Count does not refer to the addCount function object itself. This can be verified by the output.

This points to the function’s scope

On the scope issue, in one case it is true, but in other cases it is false, so it cannot be said that this refers to the scope of a function. To be clear, this does not refer to the lexical scope of a function in any case.

Here’s a typical example:

function foo() {
    var a = 2;
    this.bar();
}

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

foo(); // ReferenceError: a is not defined
Copy the code

What this code does is:

First, foo wants to call bar from this.bar;

  • It is normal to call directly from bar() instead of this.bar()

Second, function bar wants to access variable A in function foo through this.a;

  • You cannot use this to refer to something inside a lexical scope

What is this

When a function is called, an active record (also known as the execution context) is created. This record contains information about the location of the function call (call stack), how the function was called, and the parameters passed in. This is one of the properties of the record and is used during the execution of the function.

Simple summary

  • This is bound at run time, not at write time, and its context depends on various conditions at the time the function is called

  • The binding of this has nothing to do with the position of the function declaration, except how the function is called

This points to at runtime

The location of the function call

To determine where the function is called, we need to understand the relationship between the stack and the position of the call. We can determine the actual position of the call from the stack because it determines the binding of this, for example:

function baz() {
    // Current call stack: baz
    // Current calling location: global scope

    console.log( "baz" );

    bar(); // <-- bar call location
}

function bar() {
    // Current call stack: baz -> bar
    // Current call location: in baz

    console.log( "bar" );

    foo(); // <-- foo's call location
}

function foo() {
    // Current call stack: baz -> bar -> foo
    // Current call location: in bar

    console.log( "foo" );
}

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

This binding rule

If you want to determine the binding object for this, you must first determine where the function is called and then determine which of the following four rules you need to follow.

The default binding

When the function is called independently, this refers to a global object or undefined, which can be used as the default rule when no other rule can be applied.

Normal: this — > global object

function foo() {
    console.log( this.a );
}
var a = 2;
foo(); / / 2
Copy the code

Strict mode: this — > undefined

function foo() {
    "use strict";
    console.log( this.a );
}
var a = 2;
foo(); // TypeError: this is undefined
Copy the code

Note: The binding rules for this depend entirely on where the call is made, but on the schema

  • The default binding can only be bound to a global object if foo() is not running in strict mode
  • Strictly independent of where foo() is called.
function foo() {
    console.log( this.a );
}

var a = 2;

(function(){
    "use strict";
    foo(); / / 2}) ();Copy the code

Implicit binding

Whether the calling location has a context object, or is owned or contained by an object.

That is, when a function references a context object, the implicit binding rule binds this in the function call to that context object.

In the following example, this in foo is implicitly bound to obj:

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

var obj = {
    a: 2.foo: foo
};

obj.foo(); / / 2
Copy the code

Only the top or last level in the object attribute reference chain affects the call location, for example:

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

var obj2 = {
  a: 2.foo: foo
};

var obj1 = {
  a: 1.obj2: obj2
};

obj1.obj2.foo(); / / 2
Copy the code

Implicit binding is lost

One of the most common problems with this binding is that implicitly bound functions lose the binding object, that is, the default binding is applied, binding this to a global object or undefined, depending on whether it is in strict mode.

Function reference passing

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

var obj = {
  a: 'a in obj'.foo: foo
};

var bar = obj.foo; // Assign the function reference to the bar variable
var a = "a in global";

bar(); // "a in global"
Copy the code

Although bar is a reference to obj.foo, it actually refers to the function foo itself, so bar() is equivalent to foo(), not a call to obj.foo().

Callback to the built-in function

Passing a defined function as a parameter to a built-in JS function (such as setTimeout) or a self-declared function is essentially equivalent to passing a function reference. Even if the function is ultimately called at a different location, it is equivalent to calling the function itself directly, that is, applying the default binding rules.

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

var obj = {
  a: 'a in obj'.foo: foo
};

var a = "a in global";

setTimeout(obj.foo, 0);// a in global
Copy the code

Explicitly bound

If you want to explicitly bind this, you can do so using the bind(), call(), and apply() methods on the function prototype.

The following example demonstrates three explicit bindings:

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

var obj1 = {
  a: 1
};

var obj2 = {
  a: 2
};

var obj3 = {
  a: 3
};

foo.bind(obj1)();/ / 1
foo.call(obj2);/ / 2
foo.apply(obj3);/ / 3
Copy the code

Note:

The bind, call, and apply methods pass in the object they want to bind to. If they pass in a primitive value, the primitive value will be boxed by default to (new String(..)). , new Boolean (..) Or new Number (..) ).

The new binding

In JavaScript, constructors are just functions that are called when the new operator is used. There are really no “constructors”, only “constructor calls” to functions.

Calling a function (or a constructor call of a function) with new automatically does the following:

  1. Create a brand new object
  2. The new object will be connected by the implementation [[prototype]]
  3. The 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
function foo(a) {
  this.a = a;
}
var bar = new foo(1);
console.log(bar.a); / / 1
Copy the code

Priority of the this binding rule

The above describes the four binding rules for this. If a call location conforms to more than one rule, they can only be distinguished by priority, in the following order:

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

The following example demonstrates the priority of explicit > implicit > default binding.

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

var a = 1;

var obj1 = {
  a: 2.foo: foo
};

var obj2 = {
  a: 3.foo: foo
};

// Default binding
foo(); / / 1

// Implicit binding
obj1.foo(); / / 2
obj2.foo(); / / 3

// Explicit binding
obj1.foo.call( obj2 ); / / 3
obj2.foo.call( obj1 ); / / 2
Copy the code

The following example demonstrates the precedence of the new binding > explicit binding.

function foo(something) {
  this.a = something;
}

var obj1 = {
  a: 1.foo: foo
};

var obj2 = {
  a: 2
};

// Implicit binding
obj1.foo(2); // obj1.a = 2
console.log(obj1.a); / / 2

// Explicit binding
obj1.foo.call( obj2, 3 ); // obj2.a =3
console.log(obj2.a); / / 3

/ / new binding
var bar = new obj1.foo(4); // bar.a = 4

console.log(obj1.a); / / 2
console.log(bar.a); / / 4
Copy the code

Exception to this binding

The this binding is ignored

When this is bound using display bindings (bind, call, apply), if null or undefined is passed to the first parameter, they are ignored when called and the default binding rules are actually applied.

function foo() {
  console.log(this.a);
}
var a = 1;
foo.call(null); / / 1
Copy the code

The function is referenced indirectly

It is worth noting that it is sometimes possible to create an “indirect reference” to a function by accident or by accident, in which case the default binding rules apply when the function is called, depending on whether it is in strict mode or not.

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

var a = 0;

var obj1 = {
  a: 1.foo: foo
};

var obj2 = {
  a: 2
};

obj1.foo(); / / 1
(obj2.foo = obj1.foo)(); / / 0
Copy the code

The assignment expression obj2.foo = obj1.foo returns a reference to the target function, so the call location is foo(), not obj2.foo() or obj1.foo().

Arrow functions in ES6

Instead of using the function keyword, arrow functions are defined using an operator called the “fat arrow” (=>).

  • Instead of using the four binding rules for this, it determines the direction of this based on its outer (functional or global) scope.
  • It doesn’t passnewOperation, because the arrow function does not have its ownthis.
  • It does not have the arguments list of normal functions.
function foo() { Return an arrow function
  return (a) = > { // This inherits from foo()
    console.log(this.a);
  };
}
var obj1 = {
  a: 1
};
var obj2 = {
  a: 2
};
var bar = foo.call(obj1);
bar.call(obj2); / / 1
Copy the code