【问题标题】:Local variable lost in closure局部变量在闭包中丢失
【发布时间】:2016-09-02 22:58:03
【问题描述】:

变量 cont 在以下内容中丢失:

    __factory.setupMenu = function(cont,input,multiSelect,exclusive,popMenu){               
        var __menu = {multiSelect:multiSelect};
        spotter.events.setEventTrigger(input,'change');
        __menu.exclusive = {inputs:[],values:exclusive||[],simpleValues:[]};
        alert(cont);//<-- is defined here
        window.popSelectComponent= cont;//<-- saved it globally to test reference

        return function(ajaxResult){
            var data = ajaxResult.template.response||[];
            var info = {},l=data.length;
            while(--l > -1){
                info[String(data[l].value)] = data[l].abbr||data[l].name;
            }

            var textTarget;
            alert(window.popSelectComponent);//<-- this is defined as expected
            alert(cont);//<-- is now undefined
            alert(input);//<-- this is defined as expected
            if(!(textTarget = cont.querySelector('[data-pop-selected]'))){textTarget = cont;}

if(!input.popSelectTemplate){   
                spotter.data.bindElementToInput(textTarget,input,function(content){
                    content = content.split(',');
                    var l=content.length;
                    while(--l > -1){
                        content[l] = info[content[l]];
                    }
                    content = content.join(',');
                    return (content.length ? content : 'ignore');
                });
            }
            else{
                var cont = document.createElement('SPAN');//<-- PROBLEM IS CAUSED HERE. HOISTING IS CAUSING CONT TO BE UNDEFINED AT CLOSURE START
                cont.className="multi-select";
                cont.appendChild(cont);

                //removal function
                var remove = (function(input){
                    return function(e){
                        var evt = e ? e:window.event;
                        if (evt.stopPropagation)    evt.stopPropagation();
                        if (evt.cancelBubble!=null) evt.cancelBubble = true;
                        if(input.value !== input.spotterPopSelectDefaultValue){ 
                            input.value = input.value.removeListValue(this.getAttribute('data-id'),',');
                            spotter.deleteElement(this);
                            if(input.value === '' && input.value !== input.spotterPopSelectDefaultValue){
                                input.value = input.spotterPopSelectDefaultValue;
                                input.eventTriggers['pop-select-change']();
                            }
                        }
                    };
                }(input));

                input.spotterPopMenuOptions = __menu;
                input.addEventListener('pop-select-change',(function(cont, info, template){ 
                    return function(){
                        var HTML = '';
                        this.value.split(',').forEach(function(val){
                            HTML += template.replace('$[ID]', val).replace('$[NAME]', info[val]);
                        });
                        cont.innerHTML = HTML;
                        spotter.castToArray(cont.children).forEach(function(el){ console.log('option el',el); el.addEventListener('click',remove,false); });

                        console.log('input.spotterPopMenuOptions',input.spotterPopMenuOptions);
                    }; 
                }(cont, info, input.popSelectTemplate.innerHTML)),false);
            }
....

所以运行var func = __factory.setupMenu(...)({template:{}}) 我收到一条错误消息,提示 cont 未定义,而 window.popSelectComponent 的定义如预期。我尝试更改 cont 的名称,以为我忽略了一些正在改变值但也不起作用的东西。

运行该函数后,我在最初创建此闭包的上下文中检查 cont 并且 cont 仍被定义,因此据我所知,这不是对象引用丢失的问题。

【问题讨论】:

  • 尝试console.log(...) 而不是使用警报。它会让你的生活更轻松,你不会后悔的。
  • 您的代码实际上在该函数中是否有alert(cont),或者您是否尝试在调试时在控制台中运行它。我怀疑发生了这样的事情:stackoverflow.com/a/21918024/1715579
  • 我只添加了警报来测试价值,因为我目前正在运行大量的 console.log 消息:P
  • @p.s.w.g 不是这样,因为他确实在闭包的最后一行使用了cont
  • 这看起来很棘手。您可以制作一个 jsfiddle 或提供一个指向实时站点的链接吗?

标签: javascript closures


【解决方案1】:

也许一个高度简化的例子会使问题更加明显:

var outer = function(theVariable) {
  console.log("In the outer function, theVariable is", theVariable);
  var inner = function() {
    console.log("In the inner function, theVariable is", theVariable);
    if (false) {
      var theVariable = 2;
    }
  };
  inner();
}
outer(1)
In the outer function, theVariable is 1
In the inner function, theVariable is undefined

如您所见,在内部函数中声明了具有相同名称的不同变量(即使未初始化)这一事实隐藏了外部函数中正确初始化的变量,否则这些变量本来是可见的。

你可能会认为因为变量是在块中声明的,所以它不会影响函数的其他部分。不,var 是函数作用域,而不是块作用域。

此缺陷已在现代版本的 Javascript 中得到解决,var 关键字已被 let 取代,let 具有您所期望的块范围。保留var 是为了向后兼容,但您不应在新代码中使用它。

【讨论】:

  • 我怀疑他是否认为该块是相关的,而仅仅是var 语句是在console.log 之后执行的。提升使var 声明的位置无关紧要。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-30
  • 2016-06-04
  • 2011-09-30
  • 2011-01-21
  • 2018-12-24
  • 1970-01-01
相关资源
最近更新 更多