javascript闭包的这种解释正确吗?

javascript闭包的这种解释正确吗?,javascript,closures,Javascript,Closures,对于tl;dr,在为表中的行创建单击处理程序时,使用闭包会使函数指向内存中的同一位置,而不是像下面的代码那样每次指向不同的位置 否则: 我正在学习javascript,我想我理解闭包是什么,为什么它有用。这是我的理由;对吗 对于HTML表,下面的代码始终显示被单击的最后一行,即使我单击了第一行或第二行。我的推理是代码创建了3个不同的堆栈帧,i分别等于0、1和2。由于2是最近的堆栈帧,因此fnPtr的返回值指向2 var table = document.getElementById("table

对于tl;dr,在为表中的行创建单击处理程序时,使用闭包会使函数指向内存中的同一位置,而不是像下面的代码那样每次指向不同的位置

否则:

我正在学习javascript,我想我理解闭包是什么,为什么它有用。这是我的理由;对吗

对于HTML表,下面的代码始终显示被单击的最后一行,即使我单击了第一行或第二行。我的推理是代码创建了3个不同的堆栈帧,i分别等于0、1和2。由于2是最近的堆栈帧,因此fnPtr的返回值指向2

var table = document.getElementById("tableid3");
var rows = table.getElementsByTagName("tr");
for (var i = 0; i < rows.length; i++) {
    var curRow = table.rows[i];
    //get cell data from first col of row
    var cell = curRow.getElementsByTagName("td")[0]; 
    var fnPtr = function() {
        alert("row " + i + " data="+ cell.innerHTML);
    }
curRow.onclick = fnPtr;
}
var table=document.getElementById(“tableid3”);
var rows=table.getElementsByTagName(“tr”);
对于(变量i=0;i
下面的代码(来自一个SO问题)使用闭包,只创建了一个堆栈框架,即createfunc()的框架。但是在这个框架中有一个局部变量tmp,它指向匿名函数。使用0、1和2调用createfunc()时,返回值指向同一堆栈帧(createfunc()的堆栈帧内的堆栈帧),这意味着不会创建3个堆栈帧。每次createfunc()返回时,返回值槽中的值都指向内存中的同一位置,而不是像上面的代码那样每次指向不同的位置

function createfunc(i) {
    var tmp = function() { console.log("My value: " + i); };
    console.log(tmp);
    return tmp;
}
for (var i = 0; i < 3; i++) {
    funcs[i] = createfunc(i);
}
for (var j = 0; j < 3; j++) {
    funcs[j]();                        // and now let's run each one to see
}
函数createfunc(i){
var tmp=function(){console.log(“我的值:+i);};
控制台日志(tmp);
返回tmp;
}
对于(变量i=0;i<3;i++){
funcs[i]=createfunc(i);
}
对于(var j=0;j<3;j++){
funcs[j]();//现在让我们运行每个
}

我了解闭包是如何工作的吗?

我不确定从堆栈框架的角度考虑javascript的工作方式是否是一个好主意——javascript的级别太高了

在第一个代码段中,您创建的fnPtr函数对象捕获实际变量(但尚未捕获其值),因此如果该变量以后发生更改,则在调用函数时,将使用当前时间的
i
variables值。您的循环可能已经创建了100个fnPtr函数对象,但每个对象都包含对完全相同的
i
变量的引用


第二个代码段利用了这样一个事实:当您将一个基元值(例如,一个数字)作为函数参数传递给函数时,javascript会创建它的一个副本。因此,每次调用createfunc时,都会创建变量的新副本,并且该新变量的值将设置为调用createfunc时参数所具有的任何值。我强调的是,这是一个新变量,没有其他函数引用它。因此,当您的createfunc返回一个新函数时,该新函数将保存对复制的新独占i变量的引用。

我不确定从堆栈帧的角度考虑javascript的工作方式是否是一个好主意-javascript的级别太高了

在第一个代码段中,您创建的fnPtr函数对象捕获实际变量(但尚未捕获其值),因此如果该变量以后发生更改,则在调用函数时,将使用当前时间的
i
variables值。您的循环可能已经创建了100个fnPtr函数对象,但每个对象都包含对完全相同的
i
变量的引用


第二个代码段利用了这样一个事实:当您将一个基元值(例如,一个数字)作为函数参数传递给函数时,javascript会创建它的一个副本。因此,每次调用createfunc时,都会创建变量的新副本,并且该新变量的值将设置为调用createfunc时参数所具有的任何值。我强调的是,这是一个新变量,没有其他函数引用它。因此,当createfunc返回一个新函数时,该新函数将保留对复制的新独占i变量的引用。

这是关于范围的,而不是堆栈的。每个函数定义都会在JavaScript中创建一个新的作用域,并且函数始终可以访问其祖先作用域

因此,在第一个示例中,
fnPtr
的所有实例都可以从父范围访问相同的
i
。单击任何一行时,
循环已经完成,
i
将为3


在第二个示例中,每个返回的
tmp
函数都有权访问传递给工厂函数的
i
参数,这在每次迭代/调用时都会有所不同。

这是关于范围的,而不是堆栈的。每个函数定义都会在JavaScript中创建一个新的作用域,并且函数始终可以访问其祖先作用域

因此,在第一个示例中,
fnPtr
的所有实例都可以从父范围访问相同的
i
。单击任何一行时,
循环已经完成,
i
将为3


在第二个示例中,每个返回的
tmp
函数都有权访问传递给工厂函数的
i
参数,这在每次迭代/调用时都是不同的。

var curRow=table.rows[i]
应该是
var curRow=rows[i]
。从技术上讲,每个函数都是JS中的闭包,因为每个函数都可以访问在更高范围内定义的变量<第一个代码片段中的code>fnPtr
是一个闭包,因为它关闭在
i
cell
上。在第二个示例中,
tmp
是一个闭包,因为它关闭在
i
上。解决循环问题的“诀窍”是创建一个新的作用域