javascript中奇怪的变量作用域

Strange variable scope in javascript

本文关键字:变量 作用域 javascript      更新时间:2023-09-26

所以我用javascript写了这段代码:

// Additional initialization code such as adding Event Listeners goes here
            FB.api('593959083958735/albums', function(response) {
                if(!response || response.error) {
                    // render error
                    alert("Noo!!");
                } else {
                    // render photos
                    for(i=0; i<response.data.length; i++){
                        var albumName = response.data[i].name;
                        var albumCover = response.data[i].cover_photo;
                        var albumId = response.data[i].id;
                        console.log(albumName);
                        FB.api( albumCover, function(response) {
                            if(!response || response.error) {
                                // render error
                                alert("Noo!!");
                            } else {
                               // render photos
                               $("ul").append('<li>'+
                                    '<a href="testFile.HTML" data-transition="slidedown">'+
                                    '<img src= "' + response.picture + '"  />'+
                                     '<h2>' + albumName + '</h2>'+
                                     '<p> Test Paragraph</p>'+
                                     '</a>'+
                                     '</li>')
                                .listview('refresh');
                            }
                        });                                 
                    }
                }
            });

我使用facebook javascript API,将facebook页面的相册导入到我的jquery移动页面,并将它们放在listView中。如你所见,我是动态创建listView的。listView的缩略图是相册的封面照片和标题,相册的名称。

然而,这里有些可怕的错误。上面代码的结果可以在这里看到

缩略图都是正确的,但专辑名称是错误的。在每个单元格中,我得到最后一个专辑名。当我console.log(albumName)在我的代码,如你所见,我得到所有的名称正确。但在给FB的第二个电话里。api: "albumName"变量只保存最后一个专辑名。

你知道这是怎么回事吗?

看起来你有著名的i for循环问题,你只有一个对i的引用。

                for (var i = 0; i < response.data.length; i++) {  //added the var here
                    (function (i) {  //created a function
                        var albumName = response.data[i].name;
                        var albumCover = response.data[i].cover_photo;
                        var albumId = response.data[i].id;
                        console.log(albumName);
                        FB.api(albumCover, function (response) {
                            if (!response || response.error) {
                                // render error
                                alert("Noo!!");
                            } else {
                                // render photos
                                $("ul").append('<li>' +
                                    '<a href="testFile.HTML" data-transition="slidedown">' +
                                    '<img src= "' + response.picture + '"  />' +
                                    '<h2>' + albumName + '</h2>' +
                                    '<p> Test Paragraph</p>' +
                                    '</a>' +
                                    '</li>')
                                    .listview('refresh');
                            }
                        });
                    })(i);  //call the function with i
                }

问题不在于i,而在于albumName

大多数JavaScript引擎都非常宽容,允许您多次使用var关键字声明相同的变量。这就是你在这里做的事。需要注意的是,for关键字并没有引入新的作用域;因此,当您(重新)声明循环中的每个变量(albumName, albumCoveralbumId)时,您将覆盖先前存储在同一变量中的值。

这会给您带来麻烦,因为您传递给FB.api的回调关闭了这些局部变量,并且(显然)异步执行。当回调实际运行时,for循环已经完成,这些变量都被设置为它们的最后一个值。

当然,例外是albumCover,正如您已经观察到的(您说缩略图都是正确的)。这是因为您将此值同步传递给FB.api;也就是说,你在循环中传递,所以它不会被关闭。

要更好地理解JavaScript中的闭包,请参见JavaScript闭包是如何工作的?

要明确的是:epascarello的回答应该能帮你解决问题。将逻辑包装在匿名函数中确实引入了一个新的作用域(一个可以在各种场景中使用的有用技巧),因此albumName等的值对于每个回调都是不同的。我只是想指出,这不是i本身造成的问题(因为你的回调不关闭i在所有)。