Who does this point to?

Believe that many partners have been asked this question before? Especially for beginners, the this pointing problem is a real headache. For example, if you are going to attend an interview, you will be asked to answer this question.

Before, I also felt confused about the direction of this, so I read several excellent articles about this direction, and finally I have a relatively clear understanding. Now I will summarize it.

Before I begin, I recommend that you review your scope knowledge by taking a look at my previous article: a detailed explanation of JavaScript scopes and scope chains 🔗

OK, let’s get started!

📍 Judge the basic flow of this

First of all, I give a basic process of this pointing to judgment:

1. This of a normal function

  • Binding via the new operator (creates an instance object to which this in the constructor refers)
  • By explicit binding(bycall,apply,bindThese three functions specify the binding object for this.
  • By implicit binding(Refers to the object last invoked, as inobj1.obj2.foo()Is called by,fooWithin thethisPoint to theobj2)
  • The default binding(In non-strict modethisPoints to global objects, in strict modethisWill be bound toundefined)

2. Arrow function this

  • Points to the scope above which the function was defined

Next, we describe each of these steps in detail.

🖐 This of the ordinary function

1. Bind with new

When we call the constructor using the new operator, the following is done automatically:

  1. Create an empty object in the constructorthisPoint to this empty object
  2. Inside the empty object[[Prototype]]Property assigned to the constructorprototypeProperties.
  3. The constructor method is executed, and properties and methods are added tothisObject referenced
  4. Returns if no other object is returned from the constructorthisOtherwise, the object returned in the constructor is returned.

The process of creating a new constructor is another frequent interview question, but it is not the main topic of this article, so I will not describe it too much. Next, let’s look at an example:

function SayHi (name) { 
    this.name = name
} 
var Hi = new SayHi('Rocky')
console.log('Hello,', Hi.name)
Copy the code

The output is Hello, Rocky

Because var Hi = new SayHi(‘Rocky’) binds this in SayHi to the Hi object.

In addition, the result of an object created using the new operator is basically the same as that of an object created literally. Take a look at this example:

var name = 'window'
function Person (name) {
  this.name = name
  this.foo = function () {
    console.log(this.name)
    return function () {
      console.log(this.name)
    }
  }
}
var person2 = {
  name: 'person2',
  foo: function() {
    console.log(this.name)
    return function () {
      console.log(this.name)
    }
  }
}
  
var person1 = new Person('person1')
person1.foo()()
person2.foo()()
Copy the code

First, person1.foo() prints this.name, where this must refer to person1, so it prints person1, and it returns a function called globally: Person1.foo ()(), so the output is the global name, so window. The case for person2 is the same as for person1, because foo() is called by person2 and is implicitly bound (as explained below), so this refers to person2.

So, the output is:

'person1'
'window'
'person2'
'window'
Copy the code

2. Default binding

Since explicit binding requires knowledge of default and implicit binding, the order is reversed here.

The default binding, which is used when no other binding rule can be applied, is usually a stand-alone function call.

For the default binding, just remember that in non-strict mode this refers to a global object, and in strict mode this is bound to undefined. Here’s an example:

function sayHi () { 
    console.log('Hello,', this.name)
} 
var name = 'Rocky'
sayHi() 
Copy the code

Output: Hello, Rocky

When sayHi() is called, the default binding is applied. This refers to a global object (in non-strict mode). In strict mode, this refers to undefined, and undefined has no this object, raising an error.

Since sayHi() is called in the global context, we can also call it window.sayhi (), which is equivalent to this.

The above code, if run in a browser environment, will result in Hello,Rocky

But if you run it in a Node environment, the result is Hello,undefined, because the name in node is not attached to the global object.

In this document, unless otherwise specified, the default execution result is the browser environment.

Here’s another example:

var name = "windowsName"

function fn() {
    var name = 'Cherry'
    innerFunction()
    function innerFunction() {
        console.log(this.name)      // windowsName
    }
}

fn()
Copy the code

The innerFunction is called as a function that is not mounted on any object, using the default binding. In non-strict mode, this refers to the window

Notice if the variable is not usedvarDeclarative, but withlet,constDeclared, so it’s not bound towindowObject:

let a = 10
const b = 20

function foo () {
  console.log(this.a)
  console.log(this.b)
}
foo()
console.log(window.a)
Copy the code

The output is

undefined 
undefined 
undefined
Copy the code

Implicit binding

The invocation of a function is triggered on an object, that is, a context object exists at the invocation location. The typical form is xxx.fun (). Let’s look at a piece of code:

function sayHi(){
    console.log('Hello,', this.name)
}
var person = {
    name: 'Rocky',
    sayHi: sayHi
}
var name = 'will'
person.sayHi()
Copy the code

It prints Hello,Rocky

The sayHi function is declared externally and does not technically belong to Person, but when sayHi is called, the call location references the function using Person’s context, and the implicit binding binds this in the function call (this in this case sayHi) to the context object (Person in this case)

It is important to note that only the last layer in the object property chain affects the call location. Consider the following example:

function sayHi(){
    console.log('Hello,', this.name)
}
var person2 = {
    name: 'Rocky',
    sayHi: sayHi
}
var person1 = {
    name: 'will',
    friend: person2
}
person1.friend.sayHi()
Copy the code

The result: Hello, Rocky

Because only the last layer is going to determine what this refers to, no matter how many layers there are, we’re only going to focus on the last layer in determining this, which is the friend here.

Implicit binding has a case where implicit binding loss occurs. Implicit loss is when implicitly bound functions lose bound objects under certain circumstances.

Implicit loss problems occur in two situations:

  • Alias the function using another variable
  • Functions passed as arguments are implicitly assigned, and callbacks lose the this binding

Next, let’s look at a concrete example:

function sayHi(){
    console.log('Hello,', this.name)
}
var person = {
    name: 'Rocky',
    sayHi: sayHi
}
var name = 'will'
var Hi = person.sayHi
Hi()
Copy the code

The result is: Hello,will

Because Person assigns the sayHi method directly to Hi, but doesn’t call it. Since this refers to the object that last called it, and Hi is called in the global window, sayHi is called in the window. In other words, “sayHi” has nothing to do with “Person.” This refers to the window.

For this type of problem, just keep this format in mind: xxx.fn (), if there is nothing before fn(), it is definitely not implicitly bound (or implicitly bound missing). In the above code, there is no object before Hi() to call it, so it is not implicitly bound (or implicitly bound missing)

Implicit binding loss can also occur if you pass a function as an argument:

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

The output is window 2

Here we pass obj.foo as an argument to doFoo, and as we pass this inside obj.foo() we change it to point to window. So the output a is the global a.

Here’s the catch: many people think that this in obj.foo() refers to window because it was called in doFoo, and doFoo’s this is window, but that’s not the case.

Now instead of calling doFoo from window, we’ll put it in obj2 and call it from obj2

function foo () {
  console.log(this.a)
}
function doFoo (fn) {
  console.log(this)
  fn()
}
var obj = { a: 1, foo }
var a = 2
var obj2 = { a: 3, doFoo }

obj2.doFoo(obj.foo)
Copy the code

The result is:

{ a:3, doFoo: f }
2
Copy the code

Now call obj2.dofoo (), where this should refer to obj2, since obj2 called it.

But obj.foo() still prints a 2, which is a under window.

For this question, just remember the following conclusion:

Implicit loss also occurs when a function is passed as an argument to another function, regardless of the reference to this of the enclosing function (such as this of doFoo() above). In non-strict mode, the function’s this is bound to the window; in strict mode, it is bound to undefined.

Here’s a different example:

function foo() {
  setTimeout(function () {
    console.log('id:', this.id)
  }, 100);
}

var id = 21
foo() // 21
Copy the code

In the above example, function () {console.log(‘id:’, this.id)} the anonymous function is passed to setTimeout as an argument, so implicit binding loss occurs, so this should refer to the global object window, So the output is the global variable ID. We can also remember that the this of an anonymous function always refers to the window

Let’s look at a little more comprehensive example:

function sayHi(){
    console.log('Hello,', this.name)
}
var person1 = {
    name: 'Rocky',
    sayHi: function(){
        setTimeout(function(){
            console.log('Hello',this.name)
        })
    }
}
var person2 = {
    name: 'will',
    sayHi: sayHi
}
var name='skumion'
person1.sayHi()
setTimeout(person2.sayHi,100)
setTimeout(function(){
    person2.sayHi()
},200)
Copy the code

The result is:

Hello, skumion 
Hello, skumion 
Hello, will
Copy the code
  • The first output, which is easy to understand, is the case of implicit binding loss, where this holds the pointer to the window.

  • SetTimeout (fn,delay) {fn()} is equivalent to assigning person2.sayHi to a variable fn and finally executing the variable, at which point this in sayHi has no relation to person2.

  • SayHi () uses an implicit binding, so this refers to person2 and has nothing to do with the current scope.

4. Explicit binding

An explicit binding is an explicit call,apply,bind that specifies the object to which this refers.

The first argument to call,apply, and bind is the object to which the corresponding function’s this points. Call and apply work in the same way, but in different ways. The call() method takes a list of arguments, while the apply() method takes an array of arguments.

  • Syntax for call:function.call(thisArg, arg1, arg2, ...)
  • The syntax of apply:function.apply(thisArg , [ argsArray])
  • Bind syntax:function.bind(thisArg, arg1, arg2, ...)

Unlike call() and apply(), bind() creates a new function that must be called manually before it can be executed.

var name = 'Rocky', age = 23 var obj = { name: 'will', objAge: this.age, myFun: Function () {console.log(this.name + 'age' + this.age)}} var anotherObj = {name: 'skumion', age: 18}Copy the code

The output is as follows

Obj. myfun. call(anotherObj)// Skumion age 18 obj. myfun. apply(anotherObj) // Skumion age 18 Obj.myfun.bind (anotherObj)() // skumion age 18Copy the code

Let’s do another example

function sayHi(){ console.log('Hello,', this.name) } var person = { name: 'Rocky', sayHi: SayHi} var name = 'will' var Hi = person. SayHi Hi. Call (person)Copy the code

The output is Hello, Rocky

Because this is explicitly bound to Person using an explicit binding.

What happens if you bind this to null or undefined via call, apply, or bind?

var foo = {
    name: 'will'
}
var name = 'Rocky'
function bar() {
    console.log(this.name)
}
bar.call(null); //Rocky 
Copy the code

As you can see from the above example, binding this to null or undefined is ignored and the default binding rule is applied.

In addition, with explicit binding, the same binding loss that occurs with implicit binding can occur.

function sayHi(){
    console.log('Hello,', this.name)
}
var person = {
    name: 'Rocky',
    sayHi: sayHi
}
var name = 'will'
var Hi = function(fn) {
    fn()
}
Hi.call(person, person.sayHi)
Copy the code

The output is Hello, will

The reason is simple: Hi. Call (person, Person.sayhi) does refer this to person. When fn is executed, however, the sayHi method is called directly (remember: person.sayHi has been assigned to FN, and the implicit binding is missing) without specifying the value of this, which is the default binding.

What if we want the binding not to be lost? Very simply, when fn is called, it is explicitly bound.

function sayHi(){
    console.log('Hello,', this.name)
}
var person = {
    name: 'Rocky',
    sayHi: sayHi
}
var name = 'will'
var Hi = function(fn) {
    fn.call(this)
}
Hi.call(person, person.sayHi)
Copy the code

At this point, the output is Hello, Rocky

Since person is bound to this in the Hi function, fn uses call(this) to bind the object to sayHi’s function. In this case, the “sayHi” refers to the Person object.

OK, here’s another example:

var obj1 = {
  a: 1
}
var obj2 = {
  a: 2,
  foo1: function () {
    console.log(this.a)
  },
  foo2: function () {
    setTimeout(function () {
      console.log(this)
      console.log(this.a)
    }.call(obj1), 0)
  }
}
var a = 3
obj2.foo1()
obj2.foo2()
Copy the code

The result is:

2
{ a: 1 }
1
Copy the code

I believe this problem is not difficult at all for you now, but I use this example to introduce a detail question:

Since call can change the direction of this, can I write it this way? obj2.foo2.call(obj1)

Note: This is wrong! If this is the case, the value of this in foo2 will change, but we know that the value of this in foo2 is irrelevant to the value of this in setTimeout, because the timer is always called by the window.

Here’s another detail that’s easy to overlook:

function foo () {
  console.log(this.a)
}
var obj = { a: 1 }
var a = 2

foo()
foo.call(obj)
foo().call(obj)
Copy the code
  • foo()It will print normallywindowUnder thea, that is,2
  • foo.call(obj)Because it’s explicitly boundthisSo it prints outobjUnder thea, that is,1
  • foo().call(obj)The beginning will executefoo()Function, print out2But it will be rightfoo()The return value of the function is executed.call(obj)Operation, but we can seefoo()The return value of the function isundefined, so an error will be reported.

The output is:

2
1
2
Uncaught TypeError: Cannot read property 'call' of undefined
Copy the code

So we can see thatfoo.call()andfoo().call()One is for the function, the other is for the return value of the function.

Return (); return (); return ();

function foo () {
  console.log(this.a)
  return function () {
    console.log(this.a)
  }
}
var obj = { a: 1 }
var a = 2

foo()
foo.call(obj)
foo().call(obj)
Copy the code
  • The first number2Nature isfoo()Output, thoughfoo()The function also returns an anonymous function, but does not call it, just writes itfoo()(), so that it is called anonymous function.
  • Second number1isfoo.call(obj)Output due to.call()Is followed byfooSo what’s changed isfoo()Within thethisPoint to, and.call()Is going to make the functionExecuted immediatelySo print out1Similarly, it does not call the returned function.
  • Number three2isfoo().call(obj)To perform firstfoo()It was printed out at this timefoo()Within thethisOr towindow.
  • The execution of thefoo()After that, an anonymous function is returned and used later.call(obj)To change this anonymous functionthisIt points to it and calls it, so it prints1.

The output is:

2
1
2
1
Copy the code

5. Priority

At this point, we have introduced the four binding rules for this. If more than one rule is applied at the same time, the priority of the four binding rules is:

New Binding > Explicit Binding > Implicit Binding > Default binding

🖖 arrow function this

There are a few things to note about the arrow function:

  • Arrow functions don’t have their ownthisObject (see below), so you can’t use call(), apply(), or bind() to change the direction of this.
  • Should not be used as constructors, that is, on arrow functionsnewCommand, otherwise an error will be thrown.
  • UnusableargumentsObject that does not exist in the function body. If you do, use the REST argument instead.
  • UnusableyieldCommand, so arrow functions cannot be used as Generator functions.

Of the four points above, the most important is the first. For normal functions, the inner this refers to the object on which the function is running, but this is not true for arrow functions. It does not have its own this object; the internal this is the this in the upper scope of the definition. That is, the this pointer inside the arrow function is fixed, in contrast to the mutable this pointer in normal functions.

1

Function foo() {setTimeout(() => {console.log('id:', this.id)}, 100); } var id = 21 foo.call({ id: 42 }) // id: 42Copy the code

In the code above, the argument to setTimeout() is an arrow function that takes effect when foo is generated and will not execute until 100 milliseconds later. If it were a normal function, this would point to the global object Window and print 21. However, the arrow function causes this to always point to the object where the function definition is in effect ({id: 42} in this case), so it prints 42.

In the following example, the callback function is an arrow function and a normal function, compared to their internal this reference.

Function Timer() {this.s1 = 0 this.s2 = 0 SetInterval (function () {this.s2++}, 1000)} var timer = new timer () setTimeout(() => console.log('s1: ', timer.s1), 3100) setTimeout(() => console.log('s2: ', timer.s2), 3100) // s1: 3 // s2: 0Copy the code

In the above code, two timers are set inside the Timer function, using the arrow function and the normal function. The former has the scope in which the this binding is defined (the Timer function), and the latter has the scope in which the runtime is defined (the global object). So, after 3100 milliseconds, timer.s1 is updated 3 times, and timer.s2 is not updated once.

The arrow function can actually make this point immutable, and binding this makes it immutable, which is a great feature for wrapping callback functions. Here is an example where the DOM event callback function is wrapped in an object.

var handler = {
  id: '123456',
  init: function() {
    document.addEventListener('click',
      event => this.doSomething(event.type), false)
  },
  doSomething: function(type) {
    console.log('Handling ' + type  + ' for ' + this.id)
  }
}
Copy the code

The above code uses the arrow function in its init() method, which causes the arrow function this to always point to a handler object. If the callback is a normal function, running this.dosomething () will result in an error because this points to the window object (this of an anonymous function points to window).

Here is the ES5 code generated by the Babel arrow function, which makes it clear what this points to.

// ES6
function foo() {
  setTimeout(() => {
    console.log('id:', this.id)
  }, 100);
}

// ES5
function foo() {
  var _this = this
  setTimeout(function () {
    console.log('id:', _this.id)
  }, 100)
}
Copy the code

In the code above, the converted VERSION of ES5 makes it clear that the arrow function does not have its own this at all, but instead refers to the outer this.

In addition, since the arrow function does not have its own this, it cannot of course use call(), apply(), or bind() to change the direction of this.

(function() {
  return [
    (() => this.x).bind({ x: 'inner' })()
  ]
}).call({ x: 'outer' })
// ['outer']
Copy the code

In the above code, the arrow function does not have its own this, so the bind method is invalid. The inner this points to the outer this.

2. Not applicable

Since the arrow function causes this to change from “dynamic” to “static”, there are four situations where arrow functions should not be used.

The first case is to define a method of an object that includes this inside.

const cat = {
  lives: 9,
  jumps: () => {
    this.lives--
  }
}
Copy the code

In the code above, the cat.jumps() method is an arrow function, which is incorrect. 4. When calling cat.jumps(), the this inside the method points to cat if it is a normal function. If you write an arrow function like the one above, such that this points to a global object, you will not get the expected result. This is because the object does not constitute a separate scope, resulting in the scope of the jump arrow function being defined as global.

globalThis.s = 21;

const obj = {
  s: 42,
  m: () => console.log(this.s)
};

obj.m() // 21
Copy the code

In the example above, obj.m() is defined using the arrow function. The JavaScript engine does this by generating the arrow function in the global space and assigning it to obj.m. This causes this inside the arrow function to point to the global object, so obj.m() prints 21 in the global space instead of 42 inside the object. The code above is actually equivalent to the code below.

globalThis.s = 21;
globalThis.m = () => console.log(this.s);

const obj = {
  s: 42,
  m: globalThis.m
};

obj.m() // 21
Copy the code

For this reason, it is recommended that the attributes of an object be defined in the traditional way rather than using arrow functions.

The second case is when dynamic this is required, and arrow functions should also not be used.

var button = document.getElementById('press');
button.addEventListener('click', () => {
  this.classList.toggle('on');
});
Copy the code

When the code above runs, clicking on the button will cause an error because button’s listener is an arrow function, resulting in this being the global object. If changed to a normal function, this dynamically points to the button object being clicked.

The third case is when defining prototype methods, arrow functions should also not be used

function Foo (value) {
    this.value = value
}
Foo.prototype.getValue = () => console.log(this.value)

const foo1 = new Foo(1)
foo1.getValue()  // undefined
Copy the code

The fourth case is that constructors should also not use arrow functions

const Foo = (value) => { this.value = value; } const foo1 = new Foo(1) Uncaught TypeError: Foo is not a constructor console.log(foo1)Copy the code

3. This in Vue

All functions managed by Vue are best written as normal functions, so that this refers to the VM or component instance object. Such as:

computed: {
    foo () {
        console(this) // vm
    }
},
methods: {
    bar () {
        console(this) // vm
    }
}
Copy the code

All functions not managed by Vue (timer callbacks, Ajax callbacks, Promise callbacks, etc.) are best written as arrow functions so that this points to the VM or component instance object. Such as:

methods: {
    foo () {
        setTimeOut (() => {
            console.log(this) // vm
        }, 1000)
    },
    bar () {
        return new Promise ((resolve, reject) => {
            resolve()
            console.log(this) // vm
        })
    }
}
Copy the code

📒 exercises

The first question

"use strict"
var a = 10
function foo () {
  console.log('this1', this)
  console.log(window.a)
  console.log(this.a)
}
console.log(window.foo)
console.log('this2', this)
foo()
Copy the code
  • Turns on strict mode, just to say that it doesWithin the functionthethisPoint to theundefinedIt doesn’t change the globalthisPointing to. sothis1Is printed inundefinedAnd thethis2orwindowObject.
  • Plus, it doesn’t stop itaIs bound to thewindowOn the object.

The output is:

f foo() {... } 'this2' Window{... } 'this1' undefined 10 Uncaught TypeError: Cannot read property 'a' of undefinedCopy the code

The second question

var name = "windowsName"
var a = {
    name : "Rocky",
    func1: function () {
        console.log(this.name)     
    },
    func2: function () {
        setTimeout( function () {
            this.func1() 
        },100)
    }
};
a.func2()
Copy the code

The anonymous function function () {this.func1()} is passed to setTimeout as an argument, and implicit binding loss occurs, so this refers to the global window object, but func1 is not present in the window.

Func1 is not a function

The third question

function foo () {
  console.log(this.a)
  return function () {
    console.log(this.a)
  }
}
var obj = { a: 1 }
var a = 2

foo.call(obj)()
Copy the code

First foo.call(obj) prints the 1 in obj, and then foo returns an anonymous function. This in foo() is specified as obj, but the last call to the anonymous function is window. Because foo.call(obj)() is called globally.

The output is:

1
2
Copy the code

The fourth question

function foo () {
  console.log(this.a)
}
var obj = { a: 1, foo };
var a = 2
var foo2 = obj.foo
var obj2 = { a: 3, foo2: obj.foo }

obj.foo()
foo2()
obj2.foo2()
Copy the code

In obj.foo(), foo is called by obj, so this in foo refers to obj, printing 1

Var foo2 = obj.foo is a case of implicit binding loss, so foo2() calls foo() directly on window, so output 2

Var obj2 = {a: 3, foo2: obj. Foo} is also an implicit lost binding case, obj. Foo is assigned to foo2 directly, and foo2 is called by obj2, so output 3

So the output is:

1
2
3
Copy the code

The fifth problem

How many references to this in the following code?

function foo() {
  return () => {
    return () => {
      return () => {
        console.log('id:', this.id)
      }
    }
  }
}
var f = foo.call({id: 1})

var t1 = f.call({id: 2})()()
var t2 = f().call({id: 3})()
var t3 = f()().call({id: 4}) 
Copy the code

The answer is that this refers to only one function, foo’s this, and outputs three ids: 1. This is because all inner functions are arrow functions and do not have their own this. Their this is actually the this of the outermost foo function. So no matter how nested, T1, T2, and T3 all output the same result. If all the inner functions in this example are written as normal functions, then each function’s this points to a different object of the runtime.

The output is:

id: 1
id: 1
id: 1
Copy the code

The sixth question

var name = 'window'
function Person (name) {
  this.name = name
  this.foo = function () {
    console.log(this.name)
    return function () {
      console.log(this.name)
    }
  }
}
var person1 = new Person('person1')
var person2 = new Person('person2')

person1.foo.call(person2)()
person1.foo().call(person2)
Copy the code

This is a new binding combined with explicit binding, detailed analysis as follows

  • person1.foo.call(person2)()willfoo()Within the functionthisPoints to theperson2So print it outperson2, while the internally returned anonymous function is created bywindowCall, so print outwindow.
  • person1.foo().call(person2)Is to anonymize the functionthisExplicitly bound toperson2Up, so it’s going to print outperson2.

The following output is displayed:

'person2'
'window'
'person1'
'person2'
Copy the code

Number 7

var obj = {
  a: 1,
  foo: function (b) {
    b = b || this.a
    return function (c) {
      console.log(this.a + b + c)
    }
  }
}
var a = 2
var obj2 = { a: 3 }

obj.foo(a).call(obj2, 1)
obj.foo.call(obj2)(1)
Copy the code
  • Began to callobj.foo(a)will2The incomingfooFunction and assign to the type parameterbAnd, because of closures, is accessible within anonymous functionsbIs used later when calling anonymous functionscall()Changed thethisPointer to make anonymous function insidethis.afor3And pass in the last argument1, so the first line should output3 plus 2 plus 1, that is,6.
  • And the second row,obj.foo.call(obj2)Here is thefooWithin the functionthisPoints to theobj2And no arguments are passed, sobFirst theundefinedBut because of one sentenceb = b || this.a,bInto the3; And the last piece of code(1), is calling an anonymous function, and this anonymous functionthisIt should be pointing towindow, so the output is2 plus 3 plus 1for6.

The output is:

6
6
Copy the code

The eighth problem

const obj = {
  aaa() {
    setTimeout(function() {   // 1.普通函数的setTimeout

      setTimeout(function() {
        console.log(this)     
      })
      setTimeout(() => {
        console.log(this)    
      })

    })

    setTimeout(() => {      // 2.箭头函数的setTimeout

      setTimeout(function(){
        console.log(this)   
      })
      setTimeout(() => {
        console.log(this)   
      })

    })
  }
}
obj.aaa()
Copy the code

The first normal function setTimeout: this points to the window

  • Ordinary functionalThe child setTimeout: Default binding, this refers to the global objectwindow
  • Arrow functionThe child setTimeout: refers to this of the upper scope, and this of the upper scope iswindow

SetTimeout of the second arrow function: Look for this in the upper scope AAA, because AAA is called by obj, and this of the AAA method points to obj, so this points to obj

  • Ordinary functionalThe child setTimeout: Default binding, this refers to the global objectwindow
  • Arrow functionThe child setTimeout: refers to this of the upper scope, and this of the upper scope isobj

So the final output is:

window
window
window
obj
Copy the code

Question 9

var name = 'window'
var obj1 = {
  name: 'obj1',
  foo1: function () {
    console.log(this.name)
    return () => {
      console.log(this.name)
    }
  },
  foo2: () => {
    console.log(this.name)
    return function () {
      console.log(this.name)
    }
  }
}
var obj2 = {
  name: 'obj2'
}
obj1.foo1.call(obj2)()
obj1.foo1().call(obj2)
obj1.foo2.call(obj2)()
obj1.foo2().call(obj2)
Copy the code

The arrow function this cannot be changed directly by bind, call, or apply, but can be changed indirectly by changing the direction of this in the scope. Next, let’s analyze it in detail:

  • obj1.foo1.call(obj2)()The first layer is a normal function and passes.callChanged thethisPointing toobj2So it prints outobj2The second layer is the arrow functionthisAnd in the outer scopethisSame, thereforeobj2.
  • obj1.foo().call(obj2)Print out the first layerobj1The second layer is the arrow function used.callWant to changethisBut it didn’t succeed, so.call(obj2)It doesn’t work for the arrow function, so print it outobj1.
  • obj1.foo2.call(obj2)()The first layer is the arrow function, and it wants to pass through.call(obj2)changethisPointer to, but invalid, and its outer scope iswindowSo it prints outwindow, the second layer is ordinary function,thisIs the last callerwindow, so it will also print outwindow.
  • obj1.foo2().call(obj2)The first layer is the arrow function, and the outer scope iswindowTo print outwindowThe second layer is a normal function and is used.call(obj2)To change thethisPoint to, so it prints outobj2.

The following output is displayed:

'obj2' 'obj2' 
'obj1' 'obj1' 
'window' 'window' 
'window' 'obj2'
Copy the code

The first ten questions

var obj = { hi: function(){ console.log(this) return ()=>{ console.log(this) } }, sayHi: function(){ return function() { console.log(this) return ()=>{ console.log(this) } } }, say: ()=>{ console.log(this) } } let hi = obj.hi() // 1 hi() // 2 let sayHi = obj.sayHi() let fun1 = sayHi() // 3 fun1() // 4  obj.say() // 5Copy the code
  1. obj.hi()Corresponding to the implicit binding rule for this, this is bound toobjUp, so outputobj.
  2. hi()So what we’re doing here is executing the arrow function, and the arrow function is going to go up the scope and look for this, and we just figured out that this isobj, so outputobj
  3. sayHi()Is the case of implicit binding loss, in which this performs the default binding and this points to a global objectwindow
  4. fun1()So what we’re doing here is we’re doing the arrow function, and the arrow function is going to go up one level and look for this, which points towindow, so the output iswindow
  5. obj.say()I’m doing the arrow function, so there’s no this in the current block obj, so I’m just going to look up, and I’m going to find the global this, which points towindow

The output from 1-5 is as follows:

obj
obj
window
window
window
Copy the code

OK, that’s all for this article!

If you see this, thank you for taking the time to read my article. If there is anything inappropriate in my article, please feel free to comment and give me a “like” if you think it is helpful.

reference

Hi, do you really understand this? This, call, apply, bind 【 suggest 👍】

The articles

Do you really understand script tags? JavaScript scope and scope chains 🔗 Thoroughly understand scope, execution context, lexical environment 🔎 mock. js mocks back-end interface data ✨