简体   繁体   English

JavaScript中的可变范围问题

[英]Variable scope issue in JavaScript

I have quickly coded up a sort of product display thing that gets half of its input from the page, and the other half from an AJAX query. 我已经快速编写了一种产品展示的东西,它的一半输入来自页面,另一半来自AJAX查询。

Here is the code... 这是代码...

function productDisplay() {


    products = [];

    this.index = 0;

    setupProductDisplay();

    processListItems();

    showProduct();

    function setupProductDisplay() {

        var productInfoBoxHtml = '<div id="product-info"><h3 class="hide-me"></h3><span id="dimensions" class="hide-me"></span><div id="product-gallery"><img alt="" src="" /></div><ul id="product-options" class="hide-me"><li id="spex-sheet"><a href="" rel="external">Download full spex sheet</a></li><li id="enlarge-image"><a href="" rel="lightbox-gallery">Enlarge image</a></li></ul><div id="product-description" class="hide-me"></div><span id="top"></span><span id="bottom"></span><span id="side"></span><span class="loading"></span></div>';
        $('#products').after(productInfoBoxHtml);
    }

    function processListItems() {

        $('#products > li')
            .append('<span class="product-view">View</span>')
            .filter(':even')
            .addClass('even')
        .end()
            .each(function() {

                products.push({
                    id: $(this).find('h3').html(),      
                    title: $(this).find('h3').html(),
                    dimensions: $(this).find('.dimensions').html(),
                    description: $(this).find('.product-description').html()
                });

        })
        .find('.product-view')
            .click(function() {

                var $thisListItem = $(this).parents('ul li');

                var index = $('#products > li').index($thisListItem);

                this.index = index;

                showProduct();


            });

    };


    function showProduct() {

          var index = this.index;

          console.log('INDEX = ' + index);

        // hide current data
            $('#product-info')
            .show()
            .find('.hide-me, #product-gallery')
                .hide()
            .parent()
                .find('.loading')
                .show();



            // get data contained in the page

            $('#product-info')
            .find('h3')
                .html(products[index].title)
            .parent()
            .find('#dimensions')
                .html(products[index].dimensions)
            .parent()
            .find('#product-description')
                .html(products[index].description)


            // get id & then product extra info

            var id = $('#products > li').eq(index).attr('id').replace(/id-/, '');




            var downloadPath = PATH_BASE + 'downloads/';

            var imagePath = PATH_BASE + 'images/products/'

            $.getJSON(PATH_BASE + 'products/get/' + id + '/',
                function(data){           
                  var file = '';    
                  var images = [];

                  file = data.file;

                  images = data.images;

                  // show file list item if there is a file
                  if (file) {
                    $('#spex-sheet').show().find('a').attr( { href: downloadPath + file  } );   
                  } else {                  
                    $('#spex-sheet').hide();
                  }

                  // image gallery



                  if (images.length != 0) {
                    $('#product-gallery').show();
                    // preload image thumbnails
                    $.each(images, function(i, image){
                        var img = new Image();
                        img.src = imagePath + 'thumb-' + image;
                        img = null;
                    });

                    // set first image thumbail and enlarge link
                    if (images[0]) {
                        $('#enlarge-image').show().find('a').attr({ href: imagePath + images[0] });
                        $('#product-gallery img').attr ( { src: imagePath + 'thumb-' + images[0]} )

                    }

                    console.log(images);

                    // setup gallery

                    var currentImage = 0;

                    clearInterval(cycle);

                    console.log(cycle);



                    var cycle = setInterval(function() {
                        console.log(currentImage + ' = ' + index);
                        if (currentImage == images.length - 1) {            
                            currentImage = 0;               
                        } else {                
                            currentImage ++;                
                        };

                        var obj = $('#product-gallery');

                        var imageSource = imagePath + 'thumb-' + images[currentImage];          
                        obj.css('backgroundImage','url(' + imageSource  +')');      
                        obj.find('img').show().fadeOut(500, function() { $(this).attr({src: imageSource}) });
                        $('#enlarge-image a').attr({ href: imagePath + images[currentImage] });         
                    }, 5000);


                    // setup lightbox
                    $("#enlarge-image a").slimbox({/* Put custom options here */}, null, function(el) {
                        return (this == el) || ((this.rel.length > 8) && (this.rel == el.rel));
                    });



                  } else {
                    // no images

                    $('#enlarge-image').hide();
                    $('#product-gallery').hide();

                  };


                  // show the product info
                  $('#product-info')
                    .find('.hide-me')
                        .remove('#product-gallery, #spex-sheet')
                            .show()
                 .parent()
                    .find('.loading')
                        .hide();

            });


    };




};

The important function is showProduct(). 重要的函数是showProduct()。 Now generally I don't write JS like this, but I decided to give it a go. 现在,通常我不会写这样的JS,但是我决定尝试一下。 My problem is, that when a user clicks a 'more' button, and it displays the prouduct, it doesn't reset the simple slideshow (the images var is reset, I think it has to do with the setInterval() maybe, or it seems it's making a new instance of showProduct() everytime). 我的问题是,当用户单击“更多”按钮并显示产品时,它不会重置简单的幻灯片显示(图像var被重置,我认为这可能与setInterval()有关,或者似乎每次都在制作showProduct()的新实例)。

Does anyone know what I'm doing wrong? 有人知道我在做什么错吗?

I had to reformat your code to really understand what was going on. 我必须重新格式化您的代码才能真正了解发生了什么。 Anyway, I found the problem with the code. 无论如何,我发现了代码问题。

As you guessed correctly, problem is with the scope but not with the variable 'images' but with variable 'cycle'. 您猜对了,问题出在范围上,而不是变量“ images”,而是变量“ cycle”。 Why? 为什么?

This line 这条线

var cycle = setInterval(function() {

Always creates a new local cycle variable (notice the 'var') which is not accessible when showProduct gets called the second time. 始终创建一个新的局部循环变量(注意“ var”),该变量在第二次调用showProduct时不可访问。 This means that this line 这意味着这条线

clearInterval(cycle);

is essentially useless as it always passes null to the clearInterval function and doesn't clear anything. 本质上是没有用的,因为它总是将null传递给clearInterval函数,并且不清除任何内容。 This means that as you keep clicking on 'more', you are creating more and more setInterval function calls, never clearing the old ones. 这意味着,当您继续单击“更多”时,您将创建越来越多的setInterval函数调用,而不会清除旧的函数调用。

Anyway, I have refactored your code a little bit, I think this should work as expected. 无论如何,我对您的代码进行了一些重构,我认为这应该可以正常工作。 The changes I did are: 我所做的更改是:

  1. Removed this.index variable. 删除了this.index变量。 It's better to pass 'index' to showProduct instead of setting this.index before showProduct method call and making showProduct use that variable. 最好将'index'传递给showProduct,而不是在showProduct方法调用之前设置this.index并让showProduct使用该变量。 Also, why did you prefix the variable with 'this'? 另外,为什么在变量前加上“ this”呢?

  2. Declared cycler variable outside the scope of showProduct, local to the productDisplay method. 在showProduct范围之外声明的循环器变量,在productDisplay方法本地。 This insures that you can access cycler during different showProduct calls. 这样可确保您可以在不同的showProduct调用期间访问循环仪。

  3. Created smaller functions named showFile, showGallery, showProductInfo to make it easier to understand/maintain code. 创建了名为showFile,showGallery,showProductInfo的较小函数,以使其更易于理解/维护代码。

Let me know if you have any questions OR if the code still doesn't work. 让我知道您是否有任何疑问,或者代码仍然无法正常工作。

function productDisplay() {

    //Instead of keeping this.index variable, it's better to make showProduct function
    //take index variable. 

    products = [];
    setupProductDisplay();
    processListItems();

    //We have to define cycler outside the showProduct function so that it's maintained
    //in between showProduct calls. 
    var cycler = null;

    showProduct(0);

    function setupProductDisplay() 
    {
        var productInfoBoxHtml = '<div id="product-info"><h3 class="hide-me"></h3><span id="dimensions" class="hide-me"></span><div id="product-gallery"><img alt="" src="" /></div><ul id="product-options" class="hide-me"><li id="spex-sheet"><a href="" rel="external">Download full spex sheet</a></li><li id="enlarge-image"><a href="" rel="lightbox-gallery">Enlarge image</a></li></ul><div id="product-description" class="hide-me"></div><span id="top"></span><span id="bottom"></span><span id="side"></span><span class="loading"></span></div>';
        $('#products').after(productInfoBoxHtml);
    }

    function processListItems() 
    {
        $('#products > li')
            .append('<span class="product-view">View</span>')
            .filter(':even')
            .addClass('even')
            .end()
            .each(
                function() 
                {
                    products.push({
                                    id: $(this).find('h3').html(),          
                                    title: $(this).find('h3').html(),
                                    dimensions: $(this).find('.dimensions').html(),
                                    description: $(this).find('.product-description').html()
                            });

                })
            .find('.product-view')
            .click( function()
                    {
                        var $thisListItem = $(this).parents('ul li');
                        showProduct($('#products > li').index($thisListItem));

                    }
                );

    };

    function showFile(file)
    {
        if (file)
        {
            $('#spex-sheet').show().find('a').attr( { href: downloadPath + file  } );       
        } 
        else 
        {                                      
            $('#spex-sheet').hide();
        }
    }

    function showGallery(images)
    {
        if(! images || !images.length || images.length == 0)
        {
            $('#enlarge-image').hide();
            $('#product-gallery').hide();
            return;
        }

        $('#product-gallery').show();

        $.each(images, 
                function(i, image)
                {
                    var img = new Image();
                    img.src = imagePath + 'thumb-' + image;
                    img = null;
                });

        // set first image thumbail and enlarge link
        if (images[0])
        {
            $('#enlarge-image').show().find('a').attr({ href: imagePath + images[0] });
            $('#product-gallery img').attr ( { src: imagePath + 'thumb-' + images[0]} )
        }

        var currentImage = 0;
        clearInterval(cycler);

        cycler = setInterval(
                function() 
                {
                    currentImage = currentImage == images.length - 1 ? 0 : currentImage++;
                    var obj = $('#product-gallery');

                    var imageSource = imagePath + 'thumb-' + images[currentImage];                  
                    obj.css('backgroundImage','url(' + imageSource  +')');          
                    obj.find('img').show().fadeOut(500, function() { $(this).attr({src: imageSource}) });
                    $('#enlarge-image a').attr({ href: imagePath + images[currentImage] });                 
                }, 5000);



        $("#enlarge-image a").slimbox({/* Put custom options here */}, null, function(el) {
                                        return (this == el) || ((this.rel.length > 8) && (this.rel == el.rel));
                                });

    };

    function showProductInfo()
    {
        $('#product-info')
            .find('.hide-me')
                .remove('#product-gallery, #spex-sheet')
                .show()
            .parent()
                .find('.loading')
                .hide();
    }

    function showProduct(index) 
    {
        $('#product-info')
            .show()
            .find('.hide-me, #product-gallery')
                .hide()
            .parent()
                .find('.loading')
                .show();

        // get data contained in the page
        $('#product-info')
            .find('h3')
                .html(products[index].title)
            .parent()
                .find('#dimensions')
                    .html(products[index].dimensions)
                .parent()
                .find('#product-description')
                    .html(products[index].description)

        // get id & then product extra info
        var id = $('#products > li').eq(index).attr('id').replace(/id-/, '');

        var downloadPath = PATH_BASE + 'downloads/';
        var imagePath = PATH_BASE + 'images/products/'

        $.getJSON(PATH_BASE + 'products/get/' + id + '/',
            function(data)
            {           
                showFile(data.file);
                showGallery(data.image);
                showProductInfo();

            });

    };




};

If you don't define your variables with var (eg var images = ...; ) then they will be considered global variables (members of the window object). 如果不使用var定义变量(例如var images = ...; ),则将它们视为全局变量( window对象的成员)。

If you define them with var then they are visible to the whole function (even before the variable is declared) they are declared in. 如果使用var定义它们,则它们在整个函数中可见(甚至在声明变量之前),并且在其中声明它们。

I can't immediately see what the problem is, but I would recommend minimizing the scope of your variables - if they don't need to be global then make sure they aren't global. 我无法立即看到问题所在,但我建议您最小化变量的范围-如果不需要全局变量,请确保它们不是全局变量。

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

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