简体   繁体   English

Javascript - 范围问题和将参数传递给动态创建的事件处理程序

[英]Javascript - Issue with scope and passing parameters to dynamically created event handler

In the code below you will see that I am trying to define an event handler for image.onclick which takes extra parameters, i was declaring those parameters inside of the while loop hoping javascript defined scope this way but it does not. 在下面的代码中,您将看到我正在尝试为image.onclick定义一个事件处理程序,它需要额外的参数,我在while循环中声明了这些参数,希望javascript定义范围这样,但事实并非如此。 Basically all of the event handlers here are getting the very last value that i give to variables id and section_id. 基本上这里的所有事件处理程序都获得了我给变量id和section_id的最后一个值。 Any idea on how to handle a situation where i want to be generating these handlers dynamically? 关于如何处理我想要动态生成这些处理程序的情况的任何想法?

function handlePicturesResult(){
    if (req.readyState == 4) { // Complete
        if (req.status == 200) { // OK response

            var el = document.getElementById('pictureHolder');
            while( el.hasChildNodes() ){
                el.removeChild(el.lastChild);
            }

            var tempObject = JSON.parse(req.responseText);
            var pictures = tempObject.images;
            var i=0;

            while(i<pictures.length){

                var picIndex = i;

                var image= new Image(120,80);
                image.src = 'images/' + pictures[i].url;
                image.width = 120;
                image.height = 80;
                var id = pictures[picIndex].id;
                var section_id = pictures[picIndex].sectionId;
                image.onclick = function(event){
                    deleteImage(event,id,section_id);
                }


                var txtNode = document.createTextNode(pictures[picIndex.valueOf()].id); 
                el.appendChild(image);
                el.appendChild(txtNode);
                i++;
            }
        } else {
        alert("Problem: " + req.statusText);
        }
    }

}

Yet another problem solved by closures ! 关闭解决了另一个问题!

image.onclick = function(id, section_id){
    return function(event) {
        deleteImage(event,id,section_id);
    }
}(id, section_id);

The outer function is run at once (notice the parentheses with arguments at the end), and it returns the inner function, which keeps in its scope a copy of the variables as they are at that iteration of the loop. 外部函数立即运行(注意末尾带有参数的括号),并返回内部函数,该函数在其范围内保留变量副本,就像它们在循环的迭代中一样。

JavaScript passes non-object variable by value, so by passing id and section_id to the outer function, you create a copy of them inside that function. JavaScript按值传递非对象变量,因此通过将idsection_id传递给外部函数,可以在该函数内创建它们的副本。 When you create and return the inner function, that inner function keeps in scope all variables that are in scope at the time of its creation (this is at the heart of what a closure is ), including the local-variable copies of id and section_id . 当您创建并返回内部函数时,该内部函数会在其创建时保留范围内的所有变量(这是闭包的核心),包括idsection_id的局部变量副本。 The inner function, with its unique scope, becomes the event handler for that element. 内部函数及其唯一范围成为该元素的事件处理程序。

You need to use a closure. 你需要使用一个闭包。 https://developer.mozilla.org/en/JavaScript/Guide/Closures https://developer.mozilla.org/en/JavaScript/Guide/Closures

image.onclick = function(id, s_id){
  return function(event){
    deleteImage(event, id, s_id);
  }
}(id, section_id)

Scope in javascript can be defined within the curly braces of a function, which why having an outer function execute with arguments passed to it will preserve the value of these variables at the specific time in the loop you need them. javascript中的范围可以在函数的花括号中定义,这就是为什么外部函数执行时传递给它的参数将在你需要的循环中的特定时间保留这些变量的值。

Without them, the values of id and section_id will always reference the value they have at last iteration. 没有它们,id和section_id的值将始终引用它们在最后一次迭代时所具有的值。

This is a classic JavaScript problem that stems from lack of understanding about function scope. 这是一个典型的JavaScript问题,源于对功能范围缺乏了解。 On this line: 在这一行:

deleteImage(event,id,section_id);

there is no reason that the parameters passed to deleteImage should retain the values they had at the time when the callback was created. 没有理由认为传递给参数deleteImage应保留他们在当时创建回调时的值。 The variables id and section_id are bound to the scope of handlePicturesResult ; 变量idsection_id绑定到handlePicturesResult的范围; they are variables that exist within that space, and there is only ever a single copy of each. 它们是该空间中存在的变量,每个变量只有一个副本。 Thus, when the callback runs, it will use those specific variables and the values they currently refer to (from the loop's last iteration). 因此,当回调运行时,它将使用那些特定变量及它们当前引用的值(来自循环的最后一次迭代)。

The solution is to get the variables into a scope that "saves off" their values every iteration of the loop. 解决方案是将变量放入一个范围,在每次循环迭代时“保存”它们的值。 Functions provide the only such scopes in JS. 函数在JS中提供了唯一的这样的范围。 I won't repeat the excellent solutions already provided. 我不会重复已经提供的优秀解决方案。 An alternative is to use jQuery's each for iterations, and you won't have this problem again: 另一种方法是使用jQuery的each对迭代,你将不会再有这样的问题:

$.each(pictures, function(i, picture) {

    var image= new Image(120,80);
    var id = pictures.id;
    var section_id = picture.sectionId;
    var txtNode = document.createTextNode(id);

    image.width = 120;
    image.height = 80;
    image.src = 'images/' + picture.url;

    image.onclick = function(event){
        deleteImage(event, id, section_id);
    }

    el.appendChild(image);
    el.appendChild(txtNode);

});
  • Assumes pictures is an array. 假设pictures是一个数组。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM