Javascript: every event-handler defined in for-loop is the same, uses last iteration's values

﹥>﹥吖頭↗ 提交于 2019-12-31 02:18:26

问题


I have trouble understanding the scoping rules in Javascript.

In the example below, I would assume that scope url variable is private in the for-loop. And that the onload-event function would see this private instance.

But things does not seems work like that - the alert will popup with the last url twice.

If somebody can clarify what is going on, I'll be grateful.

<html>
<head>
</head>
<body>
<script type="text/javascript">
    var testArray = ["http://g0.gstatic.com/images/icons/onebox/weather_rain-40.png", "http://g0.gstatic.com/images/icons/onebox/weather_scatteredshowers-40.png"];
    for (var i=0;i<testArray.length;i++){
        var img = new Image();
        var url = testArray[i];
        img.onload = function(){
            alert(url);
        }
        img.src = url;
    }
</script>
</body>
</html>

回答1:


Javascript is not block-scoped, and thus requires a new function every time you want a new scope. See the answer by patrick dw.

This is why it is advantageous to use [].map(function(x){...}) or [].forEach(function(x){...}) which are in the javascript standard, since you'll need to define those functions anyway.

var imageArray = urlArray.map(function(url) {
    var image = new Image();
    image.src = url;
    image.onload = function() {
        alert(url);
    };

    return image;
});



回答2:


JavaScript does not have block-scope.

The only way to create new variable scope is in a function.

var testArray = ["http://g0.gstatic.com/images/icons/onebox/weather_rain-40.png", "http://g0.gstatic.com/images/icons/onebox/weather_scatteredshowers-40.png"];

function createImg( url ) {
    var img = new Image();

    img.onload = function(){
        alert(url);
    }
    img.src = url;
    return img;
}
for (var i=0;i<testArray.length;i++){
    var img = createImg(testArray[i]);
}

Passing the testArray[i] to a function that creates and returns the new image ensure that the url referenced in the onload handler will be the one that was scoped in the function.


EDIT:

Ultimately, you'd never do this if all you need is access to the url.

You'd just get it from the property of the element via this.

function onloadHandler(){
    alert( this.src );  // <--- get the url from the .src property!
}

var testArray = ["http://g0.gstatic.com/images/icons/onebox/weather_rain-40.png", "http://g0.gstatic.com/images/icons/onebox/weather_scatteredshowers-40.png"];
for (var i=0;i<testArray.length;i++){
    var img = new Image();
    var url = testArray[i];
    img.onload = onloadHandler;
    img.src = url;
}

This way you're not creating an identical handler function instance in the loop, but rather sharing the same instance, and referencing the element that received the event via this.




回答3:


Try this :)

var testArray = ["http://g0.gstatic.com/images/icons/onebox/weather_rain-40.png", "http://g0.gstatic.com/images/icons/onebox/weather_scatteredshowers-40.png"];
for (var i=0;i<testArray.length;i++){
    var img = new Image();
    var url = testArray[i];
    img.onload = function(){
        alert([img.src, url, i]);
    }
    img.src = url;
}


来源:https://stackoverflow.com/questions/6163720/javascript-every-event-handler-defined-in-for-loop-is-the-same-uses-last-itera

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!