search

Home  >  Q&A  >  body text

javascript - setInterval里面this指向的是window,下面的函数怎么理解

function Aaa(){
    this.a = 12;
    setInterval(this.show,1000);
}
Aaa.prototype.show = function(){
    console.log(this.a);
}
var a = new Aaa();
    

按照理解window里没有show的方法,结果打印出来的是a对象,理解不了,求大神指点

ringa_leeringa_lee2817 days ago1470

reply all(11)I'll reply

  • 巴扎黑

    巴扎黑2017-04-10 17:24:18

    setInterval里面的this指向的是window对象。

    准确说应该是setInterval第一个回调函数运行时,内部上下文this, 而不是传参时那个this。

    // this指向当前上下文
    setInterval(this.show, 1000)
    
    // this指向window
    setInterval(function(){
        this.show();
    }, 1000);

    更正一下其中的错误,

        setInterval(this.show, 1000) // 这个this指向当前上下文没问题
        // 等价于
        setInterval(function(){
            console.log(this.a); 
             //这里的this还是指向window 实际输出的是window.a 
             // 前面定义时var a = new Aaa()被误导了
             // 修改一下变量名 var aa = new Aaa(); 应该输出undefined;
        }, 1000)

    reply
    0
  • 伊谢尔伦

    伊谢尔伦2017-04-10 17:24:18

    回答这个问题,只用个二三句话是很难回答得清楚,要用旁敲侧击的讲法,说实在都有点对又不太对。

    我猜想你应该是想要打印12的结果,而不是现在的Aaa {a:12}这个结果。问题在于setInterval你猜对了。

    最大的问题来源是,JS是个脚本语言,定义是定义,执行是执行,对于函数更是如此,你作了函数的定义或当值来传递,并不代表它一定在执行时不会因此改变,this值就是有可能改变结果的变因。

    这段代码会怎么执行?顺序又是什么,大致上模拟一下:

    到`Aaa.prototype.show`这行,prototype物件先指定show成员,其中内容为`function(){console.log(this.a)}`
    
    到`var obj = new Aaa();`这行,读取Aaa()中函数定义,并把函数中定义的`this`自动指向到obj变量。
    
    进入到Aaa()函数区块中,`this.a=12`,所以指定`obj.a=12`,连住原型链。
    
    Aaa()函数区块中执行`setInterval(this.show,1000);`,`this.show`作为参数,相当于obj.show中的属性成员,obj中没有,往原型链上找,找到prototype物件中有,所以setInterval中相当于以下的指定:
    
    `setInterval(function(){console.log(this.a)}, 1000)`
    
    `setInterval`中的执行函数代码送到工作伫列去,等会主执行线完成后再回来执行上下文堆叠中执行。
    
    Aaa()函数区块中执行毕,刚刚`setInterval`中的执行函数代码回来执行上下文堆叠。
    
    执行`console.log(this.a)`,作用域于全局,this指向window,输出window.a,相当于`Aaa {a:12}`
    
    之后,`setInterval`中执行函数代码重覆执行。

    呃,上面看完了。那如何让setInterval(this.show,1000);在执行时能打印出this.a的值,说起来简单,改成像下面这样就行了,但为何能这样作,说起来也是满大一串的:

    setInterval(this.show.bind(this),1000);

    下面题外话,只是撰写调试的小建议供参考。

    第1是为什么要用同样的变量名称?这例子里出现了二个a变量,一个在函数里,一个在外面(全局),英文字元有26个,再加上可以大小写与各种长短不一的组合,一定要用同样的变量名称,是要考验自己还是考验来看代码的人?在show函数中到底是想要输出函数中的a,还是外面的对象a?

    第二是为什么在调试时要用setInterval而不用setTimeout,这两个不是类似的功用,差异是setTimeout只执行一次而已。问我说这么讲究作什么?问题是setInterval是一直执行下去,同样的代码你执行一次看到结果不对,难道第二次有可能变正确?而且这在调试时非常不便,我如果把代码贴到主控台测,它一直执行不停,不就又要想办法先关掉它再作第二次修正再执行。

    我都要先改写过代码如下才能调试:

    function Aaa(){
        this.a = 12;
        setTimeout(this.show,1000);
    }
    Aaa.prototype.show = function(){
        console.log(this.a);
    }
    var obj = new Aaa();

    补充说明

    setInterval的第一个传入值,要是个回调函数,等到时间到要移回主执行线中执行的。回调函数,预设呼叫它的必是window物件,也就是函数区块中的this预设是window

    看到很多回答的人也误解了,顺便谈一下,下面这两个例子有何不同?

    //第1例
    setInterval(this.show, 1000);
    
    //第2例
    setInterval(function(){this.show();}, 1000);

    第1例在执行到这行时,实际会转变为下面这样,这才算函数:

    //第1例
    setInterval(function(){console.log(this.a);}, 1000);

    在本题目中,最后的第1例与第2例的this值都是window对象,所以分别要执行的是下面这两个,结果写后面注解:

    //第1例
    console.log(window.a); //Aaa {a:12}
    
    //第2例
    window.show(); //报错,因为show函数不存在(全局中没有)

    真正要解让setInterval能够是输出a对象中的a属性,只有一种途径,让传入的这个回调函数的this值钉住,让它必定指到执行当下的对象,所谓的钉住主要是要取得在执行的那一时刻的this值,你可以记住它或直接把原来的输出语句改写。

    目前我知道有三种解决的方案,以下列出来:

    1. 用bind方法。这是个函数中自带的方法,可以用个物件来"绑定"某个函数后,回传一个新已绑定的函数。一般用这个方案,不过要习惯一下。

    setInterval(this.show.bind(this), 1000);

    2. 用setInterval的第三个传入参数值,把this传入作为that用,执行的是that.show。注意这方式在IE9之前的浏览器版本不相容。

    //最后一个传入this进去
    setInterval(function(that){that.show();}, 1000, this);

    注: that随便变量名,有人也用self_that等等

    3. IIFE解法,也是要传入this到IIFE中,与上面概念类似。IIFE一执行时就会固定住that,这里是执行that.show(),而不是有可能会变的this.show():

    (function (that){
        setInterval(function(){that.show();}, 1000);
    })(this);

    注: that随便变量名,有人也用self_that等等

    以上,算补充内容,有兴趣可以套用试试。

    reply
    0
  • 迷茫

    迷茫2017-04-10 17:24:18

    当执行到 new Aaa()时,function Aaa中的this就已经绑定成新创建的object了。

    reply
    0
  • 伊谢尔伦

    伊谢尔伦2017-04-10 17:24:18

    function Aaa(){
        this.a = 12;
        setInterval(this.show.bind(this),1000);
    }
    

    可以绑定 this

    reply
    0
  • 黄舟

    黄舟2017-04-10 17:24:18

    this指的是,调用函数的那个对象

    Javascript的this用法

    reply
    0
  • 黄舟

    黄舟2017-04-10 17:24:18

    • setInterval里的函数this指向window,没错,这也正是打印a对象的原因;

    • 调用了a对象(其实是原型)的show方法,这也没错,因为在Aaa里你给setInterval传的this就是a对象;

    reply
    0
  • 巴扎黑

    巴扎黑2017-04-10 17:24:18

    function Aaa(){

    this.a = 12;
    setInterval(this.show,1000);//这里的this指向的是创建的新对象Aaa,所以调用了show方法

    }
    Aaa.prototype.show = function(){

    console.log(this.a);//这里能this指的是window了,所以this.a就是你下面创建的全局a

    }
    var a = new Aaa();//这里a引用了新对象Aaa

    reply
    0
  • 伊谢尔伦

    伊谢尔伦2017-04-10 17:24:18

    执行new Aaa()后,构造函数以及原型上的this将指向a,
    setInterval(this.show,1000)等同于setInterval(a.show,1000)

    reply
    0
  • ringa_lee

    ringa_lee2017-04-10 17:24:18

    这个打印的就是window.a

    reply
    0
  • 天蓬老师

    天蓬老师2017-04-10 17:24:18

    //注意下面两个this的不同就好了
    setInterval(this.show, 1000)
    
    setInterval(function(){
        this.show();
    }, 1000);

    reply
    0
  • Cancelreply