[removed] every event-handler defined in for-loop

2019-02-24 17:58发布

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>

3条回答
贪生不怕死
2楼-- · 2019-02-24 18:45

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;
});
查看更多
贼婆χ
3楼-- · 2019-02-24 18:50

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.

查看更多
闹够了就滚
4楼-- · 2019-02-24 18:52

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;
}
查看更多
登录 后发表回答