function Aaa(){
this.a = 12;
setInterval(this.show,1000);
}
Aaa.prototype.show = function(){
console.log(this.a);
}
var a = new Aaa();
按照理解window里没有show的方法,结果打印出来的是a对象,理解不了,求大神指点
巴扎黑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)
伊谢尔伦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
值,你可以记住它或直接把原来的输出语句改写。
目前我知道有三种解决的方案,以下列出来:
bind
方法。这是个函数中自带的方法,可以用个物件来"绑定"某个函数后,回传一个新已绑定的函数。一般用这个方案,不过要习惯一下。setInterval(this.show.bind(this), 1000);
setInterval
的第三个传入参数值,把this
传入作为that
用,执行的是that.show
。注意这方式在IE9之前的浏览器版本不相容。//最后一个传入this进去
setInterval(function(that){that.show();}, 1000, this);
注:
that
随便变量名,有人也用self
或_that
等等
this
到IIFE中,与上面概念类似。IIFE一执行时就会固定住that
,这里是执行that.show()
,而不是有可能会变的this.show()
:(function (that){
setInterval(function(){that.show();}, 1000);
})(this);
注:
that
随便变量名,有人也用self
或_that
等等
以上,算补充内容,有兴趣可以套用试试。
伊谢尔伦2017-04-10 17:24:18
function Aaa(){
this.a = 12;
setInterval(this.show.bind(this),1000);
}
可以绑定 this
黄舟2017-04-10 17:24:18
setInterval
里的函数this
指向window
,没错,这也正是打印a
对象的原因;
调用了a
对象(其实是原型)的show
方法,这也没错,因为在Aaa
里你给setInterval
传的this
就是a
对象;
巴扎黑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
伊谢尔伦2017-04-10 17:24:18
执行new Aaa()后,构造函数以及原型上的this将指向a,
setInterval(this.show,1000)等同于setInterval(a.show,1000)
天蓬老师2017-04-10 17:24:18
//注意下面两个this的不同就好了
setInterval(this.show, 1000)
setInterval(function(){
this.show();
}, 1000);