Javascript对象上下文未声明的变量

时间:2013-11-20 13:43:20

标签: javascript

我遇到了一些奇怪的事情,我想和你分享,这样你就可以帮助我沮丧。 (我花了一段时间才弄清楚出了什么问题)

我想创建一个递归函数,在嵌套的<ul>元素上呈现树状数据结构。

数据结构采用以下形式:

var contents = [
    { 
        name : 'test',
        contents : [
            {
                name : 'test > test 1'
            },
            {
                name : 'test > test 2'
            },
            {
                name : 'test > test 3'
            }                
        ]
    },
    {
        name : 'test 2'
    }        
];

我在代码块上添加了我的代码:

var dummy = {
    do : function() {
        var element = $('<ul>');
        dummy.recursive(element, contents);

        $("#content").append(element);
    },
    recursive : function( element, contents ) {
        for( i = 0; i < contents.length; i++ ) {
            var item = contents[i];

            var li = $('<li> (' + i + ') ' + item.name + '</li>');

            if( item.contents && item.contents.length ) {
                var ul = $('<ul>');
                dummy.recursive(ul, item.contents);

                li.append(ul);
            }
            element.append(li);
        }
    }
};

我发现i之后的变量dummy.recursive在第一个元素的内容上运行时不再是0而是2.这意味着虽然i是私有的上下文正在被递归函数所取代。

我还添加了一个jsfiddle来解释这里的问题:http://jsfiddle.net/MWQJC/2/

问题在于变量i未通过在函数开头执行var i = 0而显式声明。 (如下图所示:http://jsfiddle.net/h8gtd/1/

默认情况下,在对象范围内创建所有未访问的未声明变量。是这样吗?

2 个答案:

答案 0 :(得分:2)

  

默认情况下,每个未访问的变量都是正常的   在对象范围中创建。是这样吗?

没有。

每次访问(读取)变量的值时,JS引擎都会尝试在当前作用域中查找变量,然后在当前作用域的外部作用域中查找该作用域的外部作用域等,直到您找到全球范围。此时如果仍未声明变量,则抛出错误。

当您为变量设置(写入)值时,将遵循相同的过程,最后一步是不同的。如果甚至在全局范围内尚未声明变量,则将在全局范围中创建具有该名称的变量。

例如:

// global scope
!function() { // scope 1
  var x; // comment this line to log 7 instead
  !function() { // scope 2
    x = 7;
  }()
}();

console.log(x) // undefined, because it never reached the global scope

在您的特定示例中,您是正确的,问题是i尚未在递归函数中声明为变量,因此该递归函数的所有调用将使用相同的{{1}在某些外部作用域或全局作用域中定义的变量。

编辑:我一直使用“范围”一词,但忘了提及与许多其他语言不同,JavaScript没有块范围,而是具有功能范围。

答案 1 :(得分:1)

您要求closure。这是精心解释的here

报价:

  

这是一个关闭。函数不必为了返回而返回   叫封闭。只需访问您眼前的变量即可   词法范围创造了一个闭包。

function foo(x) {
  var tmp = 3;
  return function (y) {
    alert(x + y + (++tmp));
  }
}
var bar = foo(2); // bar is now a closure.
bar(10);
     

上面的功能也会提醒16,因为bar仍然可以参考   x和tmp,即使它不再直接在范围内。