Before, I was confused about this point of setTimeout. I didn’t encounter any related problems in project practice, nor was I asked about this question in the interview, so I muddled along until I met some confusing problems in the written test recently. Also remind oneself again to knowledge point of understanding really should be careful in place, can not stay on the surface.

The description of this for setTimeout in Elevation 3 is: The code that calls timeout is executed at the global scope, so the value of this in the function refers to the window object in non-strict mode and undefined in strict mode. This article only discusses the case in the non-strict mode. According to the conclusion of elevation 3, if we really understand, we can solve 90 percent of the problems we encounter, such as:

setTimeout(console.log(this),0)//window
Copy the code

let obj = {
    print:function () {
        setTimeout(function () {
            console.log('setTimeout:'+this);
        },0);
    }
}; 
obj.print() //setTimeout: windowCopy the code

function say() {
            console.log('setTimeout:'+this);
        }let obj = {
    print:function () {
        setTimeout(say,0); }}; obj.print() //setTimeout: windowCopy the code

Whether it’s a direct reference, a method call from an object, or a function reference, it’s easy to understand. Sometimes, we’ll have two cases of this, one in the context of a setTimeout call, the other in the context of a delayed execution function. In this case, we need to pay attention to the difference. The first argument in setTimeout is simply a reference to the function, and its point depends on the context in which it is called, just like any other function call.

let obj = {
      say : function() { console.log(this); // Delay the execution of this} function,print:function () {
        setTimeout(this.say,0); //setThis, in the context of the Timeout call, points to the caller, obj}}; obj.print() //setTimeout: windowCopy the code

Let’s write this in the context of the setTimeout call in the above code to point to window, and the execution of the function will not have any effect:

let obj = {
      say : function() { console.log(this); // Delay the execution of this} function,print:function () {
        setTimeout(this.say,0); //setThis, in the context of the Timeout call, points to the caller, obj}};let func = obj.print;
func() Copy the code

Here we go:

var a = 1;
function func() {let a = 2;
        setTimeout(function(){ console.log(a); console.log(this.a); },0) } func(); // Output 2 1Copy the code

var a = 1;
function func() {/ /let a = 2;
        setTimeout(function(){ console.log(a); console.log(this.a); },0) } func(); // Output 1 1Copy the code

As you can see, when this is not used, the variables in the setTimeout timeout call are looked up from the scope of the definition in the normal function call.

So what happens when you execute it as a string

var a = 2
function say(a){
  console.log(a)
}
function test() {let a = 1;
  setTimeout("say(a)",0)
}
test() //2

var a = 2
function test() {let a = 1;
  function say(a){
    console.log(a)
  }
  setTimeout("say(a)",0)
}
test()  //say is not definedCopy the code

Eval () is not defined. The reason is that javascript is actually calling eval() internally when executing as a string, and eval() is executed in the global scope window, which has no say method.

If the parameter is passed directly as an assignment, it will not report an error:

var a = 2;
function say(a){
    console.log(a)
}
function test() {let a = 1;
    setTimeout("say('hhhh')",0)
  }
test() //hhhhCopy the code

Now let’s look at what this points to when combined with the arrow function of ES6. As you know, since the arrow function is not bound to this, it captures the value of this in its context (that is, where it is defined) as its own this value. The same is true in setTimeout.

let obj = {
    name :  "jay".print:function () {
        setTimeout(() => { console.log(this.name) },0); }}; obj.print() //jayCopy the code

How do I change setTimeout’s this point

There are two answers to the previous discussion: using an intermediate variable to refer to the outer this and applying the arrow function

Methods alet obj = {
    name :  "jay".print:function () {
           let that = this;
        setTimeout(function() { console.log(that.name) },0); }}; Method 2let obj = {
    name :  "jay".print:function () {
        setTimeout(() => { console.log(this.name) },0); }};Copy the code

Another way is to apply the bind method:

Var name ="window";
 function say(){
  console.log(this.name);
}
let obj = {
  name : "jay".print : function() {setTimeout(say.bind(this),0)
  }
}
obj.print(); //jay

Copy the code

SetTimeout parameter passing problem

1.setTimeout(function,milliseconds,param1,param2,…) ; param1,param2,… This feature is optional and can be used to provide extra arguments to function. Note that this feature is not available in IE9 and before!

function say(name) {
   console.log(name)
 }s
etTimeout(say,0,'jay')Copy the code

2. String parameter:

function say(a,b) {
   console.log(a+b)
 } 
//let name = "jay"
setTimeout( "Say (3, 4)",3000) // Output 7 after 3 secondsCopy the code

Matters needing attention

Try not to use setTimeout with a string as the first argument. SetTimeout allows you to use a string as the first argument. Inside js, eval() is called to dynamically execute a string script. Executing a piece of code requires first converting the string to executable code, which is one more step than usual, and may implicitly create global variables.

When calling setTimeout recursively, remember to apply clearTimeout cleanup to avoid memory leaks due to infinite recursion.