What is this

What is this in JavaScript? Definition: This is the object to which the containing function belongs when called as method ==. = =

function fn1(){
	this.name = "halo";
}
fn1();
Copy the code
  • So let’s break up the definition
    • The function that contains it: The function that contains this is fn1.
    • Called as a method: fn1(); Here the fn1 function is called.
    • Owning object: The default owning object of a function-type call function is Window.

From the above three points, it is easy to know that this in fn1 refers to window. So if it’s a more complex scenario how do we figure out what this is pointing to?


Who does this point to

If you want to summarize the direction of this in one sentence, people who know a little about this direction can blurt it out

This points to whoever calls it.Copy the code

That is, the orientation of this is determined at call time, not at definition time. That’s true, but it’s not complete. Instead, calling a function creates a new term for the function’s own == execution context ==. The call creation phase of the execution context determines what this points to. So a more accurate summary would be:

The reference to this is dynamically determined when the function is called, depending on the execution context.Copy the code

Before the es6 arrow function, there were four ways to determine who this referred to inside a function.

  1. Function calls
  2. Context object invocation
  3. Constructor call
  4. Bind, call, and apply change this to point

1, functional call

Let’s start with a relatively simple case where a function is called directly in the global environment. In strict mode this refers to undefined, and in non-strict mode this refers to window. The following

function fn1() {
	console.log(this)}function fn2() {
	'use strict'
	console.log(this)
}

fn1() // window
fn2() // undefined
Copy the code

Look at the following example:

const age = 18;
const p = {
     age:15.say:function(){
     	 console.log(this)
         console.log(this.age)
     }
}
var s1 = p.say
s1()       
Copy the code

Here, this in the say method still refers to the window, because s1 is still executed in the global environment of the window after the say function in p is assigned to s1. So the above code ends up printing window and undefined.

One might wonder if this.age should print 18 in a global environment. This is because variables declared using const are not mounted to the window global object, so when this refers to the window, the age on the window cannot be found. The var declaration will print 18.

If you want the code to print age in p, and this in say refers to object P. Just change the way the function is called, as follows

const age = 18;
const p = {
    age:15.say:function(){
    	console.log(this)
        console.log(this.age)
    }
}
p.say()
Copy the code

The output

	{age: 15, say: ƒ}
	15
Copy the code

Because this inside say now refers to the object that last called it. ==this is dynamically determined when the function is called based on the execution context. = =

2. Context object invocation

const p = {
	age: 18.fn: function() {
		return this}}console.log(p.fn() === p)
Copy the code

The output

true
Copy the code

If you understand the functional calls in section 1. So there should be no question here. Again, the reference to ==this is dynamically determined when calling a function based on the execution context. Remember this sentence in more complex scenarios can also easily determine the “this” reference. As follows:

const p = {
    age: 20.child: {
        age: 18.fn: function() {
            return this.age
        }
    }
}
console.log(p.child.fn())
Copy the code

Regardless of how the shallow nesting relationship changes, this only wants the last object to call it, so the output is 18. Upgrade the code again:

const o1 = {
    text: 'o1'.fn: function() {
        return this.text
    }
}
const o2 = {
    text: 'o2'.fn: function() {
        return o1.fn()
    }
}
const o3 = {
    text: 'o3'.fn: function() {
        var fn = o1.fn
        return fn()
    }
}

console.log(o1.fn())
console.log(o2.fn())
console.log(o3.fn())
Copy the code

The output

o1
o1
undefined
Copy the code
  • The first one, which should be no problem, goes straight to the function that calls this.
  • The second one looks like a callo2.fn()In fact, the internal call iso1.fn(), so it’s still outputo1.
  • Third, call after assignmentfn(), equivalent to calling a function in the global environment.thisPoint to thewindow.

If the demand right now is to make

console.log(o2.fn())
Copy the code

Output O2, how to modify the code? As follows:

const o1 = {
    text: 'o1'.fn: function() {
        return this.text
    }
}
const o2 = {
    text: 'o2'.fn: o1.fn
}

console.log(o2.fn())
Copy the code

Constructor calls

function Foo() {
    this.age = 18
}
const instance = new Foo()
console.log(instance.age)
Copy the code

The output of 18. It’s not hard to know the output, but what does the new operator do when it calls the constructor?

  • Create a new object;
  • ConstructorthisPoint to this new object;
  • Add attributes, methods for new objects;
  • Returns a new object.

Note that if there is an explicit return in the constructor, there are two scenarios to analyze.

function Foo(){
    this.age = 18
    const o = {}
    return o
}
const instance = new Foo()
console.log(instance.age)
Copy the code

Undefined is printed, because if an explicit return occurs in a constructor and an object is returned, then the constructor instance created is the object returned by the return, in which case instance is the empty object o returned.

function Foo(){
    this.age = 18
    return 1
}
const instance = new Foo()
console.log(instance.age)
Copy the code

It will print 18, because if an explicit return occurs in the constructor but returns a value other than an object, then this still refers to the instance.

Conclusion: When a constructor explicitly returns a value and returns an object, thenthisI’m going to point to this returned object. If it’s not an object,thisStill pointing to the instance.

4, Bind, call, apply change this pointer

Basic usage is not covered here. Remember that bind/call/apply changes the reference to this. Call /apply changes the reference to this and calls the function directly. Bind only changes this and returns a new function. The difference between Call and apply is that the parameter format is different. See the following code:

const target = {}
fn.call(target, 'arg1'.'arg2')
Copy the code

The above code is equivalent to the following code

const target = {}
fn.apply(target, ['arg1'.'arg2'])
Copy the code

As you can see, the knowledge call takes a different form of argument, rewritten as bind as shown below

const target = {}
fn.bind(target, 'arg1'.'arg2') ()Copy the code

Not only do you call bind to pass in arguments, but you also execute the function again after calling BIND. Now that you know how to use call/apply/bind, let’s look at this code:

const foo = {
    age: 18.showAge: function() {
        console.log(this.age)
    }
}
const target = {
    age: 22
}
console.log(foo.showAge.call(target))
Copy the code

The result is output 22, which should not be difficult to understand once you have mastered the basic usage of call/apply/bind. We’ll get into more detail about the priority of this after we say this for the arrow function.

5. Arrow function this points to

Those familiar with ES6 should know that the this pointer in the arrow function no longer follows the above rules, but is determined by the outer context. Es5 code:

const foo = {  
    fn: function () {  
        setTimeout(function() {  
            console.log(this)
        })
    }  
}  
foo.fn()  / / Window {... }
Copy the code

When this appears in an anonymous function in setTimeout(), this refers to the window object. This feature is bound to cause some problems in our development, and es6’s arrow function solves this problem nicely. Es6 code:

const foo = {  
    fn: function () {  
        setTimeout(() = > {  
            console.log(this)
        })
    }  
} 
foo.fn() / / {fn: ƒ}
Copy the code

The “this” in the arrow function does not apply the above standard, but instead finds the outer context. In this code, the “this” in the arrow function finds the outer context’s call object — foo. So this here refers to foo.

== Note that == : When the arrow function changes the this pointer, the this pointer is not affected, that is, it does not change again, as illustrated in the this priority section.

Conclusion:

  • throughCall, apply, bind, newSuch as tothisThe operations pointed to are called explicit bindings;
  • Determined by contextthisThe pointer becomes an implicit binding.

If a piece of code has both an explicit and an implicit binding, how do I determine that this refers to? To look down

6. This priority

function foo (age) {
    console.log(this.age)
}

const o1 = {
    age: 1.foo: foo
}

const o2 = {
    age: 2.foo: foo
}

o1.foo.call(o2)
o2.foo.call(o1)
Copy the code

If the implicit binding takes precedence over the explicit binding, the output should be 1,2. But running the code finds the result 2,1. This means that call and apply in explicit binding have higher priority. Then look at:

function foo (age) {
    this.age = age
}

const o1 = {}

var fn = foo.bind(o1)
fn(18)
console.log(o1.age) / / 18

var f1 = new fn(22)
console.log(f1.age); / / 22
Copy the code

If we look at the code above, fn is the function returned by foo calling bind, which is equivalent to returning foo and referring this to o1. After fn(18) is executed, the age of o1 is 18, so the first output is 18. The fn function is then called via new, where the fn function is called as a constructor, and this points to the returned instance, thus untying the o1 object.

Therefore, new takes precedence over BIND.

Remember the arrow function feature from the previous section? The arrow function affecting the this pointer cannot be modified. Look at the following code:

function foo() {
    return () = > {
        console.log(this.age)
    };
}

const o1 = {
    age: 2
}

const o2 = {
    age: 3
}

const fn = foo.call(o1)
console.log(fn.call(o2))
Copy the code

The output is 2, foo’s this points to o1, and fn receives the arrow function’s this pointing to o1. == And the arrow function’s this does not change again ==, so an explicit call binding to change this does not work.


The end! This involves a lot of knowledge, and the problem of priority is also a headache. There is no shortcut, only “rote” + “slowly understand”