Why this

To answer this question, let’s first see what happens if we don’t use this. Imagine writing the following code without using this:

function speak(){
    var name = this.name
    console.log(`Hello I am ${name}`)
}
var me = {
    name: 'a',
    speak: speak
}
var you = {
    name: 'b',
    speak: speak
}
me.speak()  //Hello I am a
you.speak()  //Hello I am b
Copy the code

This can use different context objects in the same execution environment. It actually provides a more elegant way to implicitly “pass” an object reference, thus making API design simpler and easier to reuse.

Who is this

This is neither itself nor the scope of the current function. We can test it in code.

  • Judge whether it is self
function fn(){
    console.log(this.name)
}
fn.name = 'xxx'
fn()  //undefined
Copy the code
  • Determine if it is a scope
function foo() { 
    var a = 2;
    this.bar(); 
}
function bar() { 
    console.log( this.a );
}
foo(); //undefined
Copy the code

So who is this? Not necessarily,this is bound at runtime, so it depends on the execution context of the function.

When a function is called, an active record (execution context) is created. This record contains information about where the function was called (call stack), how the function was called, and what arguments were passed in. This is also a property here.

How do I determine if this points to

This refers to the execution context of the function, i.e., “who called it”.

Independent function call

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

The direct method of calling this refers to the global object or, if in the browser, to the window

Object context (implicit binding)

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

Foo is defined in the global scope, but is called through the obj context, which is understood to be owned by the OBJ object at the moment foo is called. So this points to obj. There are two questions:

  • Chain calls

In the case of chain calls, only the last layer affects the location of the call, for example:

Obj1.obj2.obj3.fn () // This in fn points to obj3Copy the code
  • Led type is missing
function foo() { console.log( this.a ); } var obj = { a: 2, foo: foo }; var bar = obj.foo; // Function alias! var a ="xxxxx"
bar(); // xxxxx
Copy the code

Bar actually refers to the address of obj.foo, which refers to a function, meaning that bar calls comply with the “independent function call” rule. So its this is not obj.

The callback function is implicitly lost

Modify the above code slightly:

function foo() { 
    console.log( this.a );
}
var obj = { 
    a: 2,
    foo: foo 
};
var a = "xxxxx"
setTimeout( obj.foo ,100); // xxxxx
Copy the code

As we can see, the callback is referred to by obj, but this is not obj. In fact, the built-in setTimeout() function implementation is similar to the following pseudocode:

function setTimeout(fn, delay){fn()}Copy the code

In fact, this code hides the operation fn=obj.foo, which is the same as bar=obj.foo in the example above.

Explicitly bound

Explicit binding is the opposite of implicit binding, which refers to explicitly changing the this reference via call, apply, or bind. The first argument to each of these methods is the object to which this refers. Note that if you pass a value (String, Boolean, number) to the first argument, the value will be converted to object form (call new String(..)). , new Boolean (..) And the new Number (..) ). The bind method is special in that it delays method execution, which allows us to write more flexible code. The principle is also easy to simulate:

function foo(something) { 
    console.log( this.a, something ); 
    return this.a + something;
}
function bind(fn, obj) {
    return function() {
        return fn.apply( obj, arguments );
    }; 
}
var obj = { 
    a:2
};
var bar = bind( foo, obj ); var b = bar( 3 ); // 2 3 console.log( b ); / / 5Copy the code

Note: If null or undefined is passed to the first argument, this value is ignored, which corresponds to the independent function call rule

The new binding

New in Js is different from the traditional class-oriented language mechanism, and the “constructor” in Js is actually no different from ordinary functions. In fact, when we call a function using new, the following happens:

  • Create a brand new object
  • The new object will be “prototype” linked
  • The new object will be bound to the calling this
  • If the function has no return value of the object type, the object will be returned

The third step binds this, so the “constructor” and this in the stereotype always refer to the instance coming out of new.

priority

The weight of the above four judgment rules is increasing. The order of judgment is:

  • The function isnewCome out,thisPointing to the instance
  • Functions are bound by call, apply, and bind.thisPoints to the first parameter of the binding
  • The function is called in some context object (implicit binding),thisPoint to context object
  • None of the above,thisPointing to a global object

Differences in strict mode

All of the above is valid in non-strict mode, and the direction of this in strict mode is different.

  • Standalone function calls:thisPoint to theundefined
  • Methods on objects:thisAlways point to that object
  • No other difference

This in the arrow function

Arrow functions are not defined by the function keyword and do not use the this rule above, but rather “inherit” the this pointer in the outer scope. In fact, although there is no arrow function, we often do the same thing as the arrow function, for example:

function foo() {
    var self = this; 
    setTimeout( function(){ console.log( self ); }, 100); }Copy the code

This in the getter and setter

The getter or setter functions in ES6 bind this to the object that sets or retrieves the property.

function sum() {
  return this.a + this.b + this.c;
}
var o = {
  a: 1,
  b: 2,
  c: 3,
  get average() {
    return(this.a + this.b + this.c) / 3; }}; Object.defineProperty(o,'sum', { get: sum, enumerable: true, configurable: true}); console.log(o.average, o.sum); // logs 2, 6Copy the code

The resources

MDN – This “javascript you Didn’t Know, Volume 1”