javascript中的递归函数,setTimeout和'this'关键字

T11*_*110 1 javascript recursion this settimeout

假设我想用onClick方法调用一个函数.像这样:

<li class="inline" onclick="mve(this);" >TEMP</li>
Run Code Online (Sandbox Code Playgroud)

我有一个JS函数,如下所示:

function mve(caller){
caller.style.position = "relative";
caller.style.left = (caller.style.left+20) +'px';
var foo = setTimeout('mve(caller)', 2000);
}
Run Code Online (Sandbox Code Playgroud)

我的问题是在初始onClick调用之后,元素(调用者引用的)是未定义的.至少这是Firebug告诉我的.

我确信这是一个简单的解决方案,那么如何简单解释为什么以及如何解释?

如果我像这样运行它:

function mve(caller){
caller.style.position = "relative";
caller.style.left = (caller.style.left+20) +'px';
}
Run Code Online (Sandbox Code Playgroud)

我认为元素在每次点击时都会移动20px,但情况并非如此.思考?

jfr*_*d00 5

setTimeout()在全局范围内执行字符串参数,因此您的值this不再存在,也不是您的参数caller.这是不使用setTimeout的字符串参数的众多原因之一.使用像这样的实际javascript函数引用,解决相关传递的参数是一个非常容易的问题:

function mve(caller){
    caller.style.position = "relative";
    caller.style.left = (caller.style.left+20) +'px';
    setTimeout(function() {
        mve(caller)
    }, 2000);
}
Run Code Online (Sandbox Code Playgroud)

对于你的问题的第二部分,caller.style.left将会有这样的单位,20px当你添加20它,你得到20px20,这不是一个浏览器将理解的值,所以没有任何反应.您将需要解析其中的实际数字,将数字加20,然后重新添加单位,如下所示:

function mve(caller){
    caller.style.position = "relative";
    caller.style.left = (parseInt(caller.style.left), 10) +20) + 'px';
    setTimeout(function() {
        mve(caller)
    }, 2000);
}
Run Code Online (Sandbox Code Playgroud)

此功能缺少的东西是它停止重复的一种方式.正如你现在所拥有的那样,它会永远持续下去.我可能会建议传递这样的迭代次数:

function mve(caller, iterationsRemaining){
    caller.style.position = "relative";
    caller.style.left = (parseInt(caller.style.left), 10) +20) + 'px';
    if (--iterationsRemaining) > 0) {
        setTimeout(function() {
            mve(caller, iterationsRemaining)
        }, 2000);
    }
}
Run Code Online (Sandbox Code Playgroud)

此外,您可能很想知道这不是一个真正的递归函数.那是因为mve()函数调用setTimeout()然后立即完成.它是setTimeout()mve()一段时间之后执行下一次迭代,并且在多个函数调用的堆栈帧上没有累积,因此没有实际的递归.从一瞥代码看起来确实像递归,但在技术上并非如此.