为了账号安全,请及时绑定邮箱和手机立即绑定

setTimeout闭包疑问

setTimeout闭包疑问

眼眸繁星 2019-04-13 08:36:42
代码如下:jsvardiv=$('#appendHere');$('#clickMe').on('click',function(){varthat=this;div.append(checkForWindow(this));setTimeout(function(){div.append(checkForWindow(this));div.append('thatisthe'+that.tagName+'');},300);});functioncheckForWindow(elm){if(elminstanceofWindow){return'thisistheWindow';}elseif(elm.tagName){return('thisisthe'+elm.tagName+'');}else{return('thisis'+elm+'');}}输出结果:(另见:http://jsfiddle.net/dposin/okjr81ev/light/)thisistheBUTTONthisistheWindowthatistheBUTTON问题来了,setTimeout中的checkForWindow(this)为什么没有形成闭包,而that又形成闭包了呢?这样写就能形成闭包?本来我以为是要这样写的:jsfor(vari=0;i
查看完整描述

2 回答

?
www说

TA贡献1775条经验 获得超8个赞

其实JS中的所有function均为(lexical)closure,所以直接理解了函数的定义与执行过程自然就理解closure的特点了。
function在定义的时候(就是生成FunctionObject的)会将当前EC的ScopeChain作为自身的[[Scope]]的属性值,其中that变量位于当前EC的AO中,因此自然也在settimeout延迟执行函数的[[Scope]]属性中。
然后在settimeout的延迟执行函数执行时会先构建自己的EC,新EC中的ScopeChain=AO+[[Scope]],然后在延迟函数中访问that时则在ScopeChain解析这个引用,那自然能找到之前设置的值。
而this是关键字,跟变量是不同的,因此不会在ScopeChain上解析。在EC中有一个ContextObject的属性,我们访问this其实就是访问EC的ContextObject,这个属性是根据函数的调用方式来决定的(暂且这样理解吧,其实底层有一套比较难理解的规范的)
functioncallMeBaby(){console.log(this)}
callMeBaby()//显示[objectWindow],this指向window
host={callMeBaby:callMeBaby}
host.callMeBaby()//显示[objectObject],this指向host
上面可以理解为this就是指向函数调用时的所属对象,如果没有所属对象则指向全局对象(window)。下面示例就只能理解底层原理后才能理解透了,先留个坑吧
(1,host.callMeBaby)()//显示[objectWindow],this指向window
                            
查看完整回答
反对 回复 2019-04-13
?
开满天机

TA贡献1786条经验 获得超13个赞

说说个人的理解,楼主可能对闭包的理解有些问题,闭包是指返回的函数能够保留对其父函数中变量的引用
而楼主说的问题,其实是this的指向问题,和闭包没有什么关系,现在说说我对上面现象的理解:
div.append(checkForWindow(this));这个里面的this是调用触发当前函数的element,也就是BUTTON;
然后varthat=this;这里将element赋值给that,这里的that就是个普通的变量名,不是关键字,没有任何特殊意义;
接下来到setTimeout语句,其中传入的为一个匿名函数,这个不是什么闭包就是个函数,然后这个定时器在300ms之后被调用了,里面又调用了div.append(checkForWindow(this));,这个时候,this的指向已经变了,根据this的定义,this是指向调用当前函数的对象,而对于这种匿名函数的调用,这时this指向的就是顶级的window;因此打印出来就是Window;
最后的that,这个和闭包也没有什么关系,这个根据作用域的知识,可以知道在匿名函数中没有这个变量,因此去更加高一级去寻找这个变量,也就是指向BUTTON的this;
最后说说楼主的这个函数
for(vari=0;i<10;i++){
setTimeout((function(i){
console.log(i);
})(i),0);
}
这个其中的
(function(i){
console.log(i);
})(i)
这个叫立即执行函数,其中的i会被存储起来,是一个闭包,因此会打印出0到9的数字,但是这个写法对于定时器来说也是有问题的,你会发现不论你的时间间隔设置多长,都是瞬间完成,因为这个函数直接执行了,其实赋值给定时器是一个undefined,也就是说,setTimeout其实没有执行函数,你看到的打印数字是在你定义setTimeout的时候打印出来的
正确的写法是
for(vari=0;i<10;i++){
setTimeout((function(i){
returnfunction(){
console.log(i);
}
})(i),xxtime);
}
这样写会在xxtimems之后连续执行10个定时器
                            
查看完整回答
反对 回复 2019-04-13
  • 2 回答
  • 0 关注
  • 392 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信