javascript - 如何使此代码工作?

时间:2013-05-09 23:02:34

标签: javascript jquery closures

代码给了我: 一个 乙 ç

当我点击A B C时,它总是显示最后一个“伏特加”。 我想要“马丁”(A),“林赛”(B),“伏特加”(C) 请帮我解释一下我的例子。

myArray = [
    {
        letter: "A",
        brand: "martin"
    },
    {
        letter: "B",
        brand: "lindsay"
    },
    {
        letter: "C",
        brand: "vodka"
    }
];
    var list = '';
    for (var i = 0; i < myArray.length; i++) {
    list += "<br>" + myArray[i].letter;

    new_info = myArray[i].link;
    (function(new_info) {
         $(this).click(function(){        //this - refers to A or B or C
                 $('#box2').text(new_info);
                 });
    }).call(this, myArray[i])
}

$('#box1').append(list);

1 个答案:

答案 0 :(得分:2)

编辑:
我说我不会为你编写代码......好吧,我做了:this fiddle完全正是你要找的。我用上下文(this),闭包问题和隐含的全局变量解决了这个问题。它仍然需要做很多工作,但是小提琴显示了每个人都在说的内容:$(this) 没有,不能也不会指向像"A", or "B" 这样的字符串常量。 / p>

很抱歉这么说,但您的代码完全有问题,但我会在这里解决您要问的具体问题。
在循环内部,您将分配一个单击处理程序,基本上如下所示:

function()
{
    $('#box2').text(new_info);
}

其中new_info是在更高范围内声明的变量。到现在为止还挺好。问题是,您正在创建的函数对象没有自己的 copy ,无论该变量(new_info)在创建该函数时碰巧保留了什么值。相反,该函数引用该变量。因此,当调用任何这些函数时,$('#box2').text(new_info)将被解析为$('#box2').text("whatever value new_info holds when function is called"),而不是$('#box2').text("whatever value new_info was holding when function was created")。只需在代码中添加第二个函数,即可为每个回调访问副本

$(this).click((function(currentNewInfo)
{
    return function()
    {
        $('#box2').text(currentNewInfo);
    }
}(new_info)));

我在这里做的是创建一个函数,它接受一个参数,并立即调用它。我将new_info作为参数传递,因此currentNewInfo的值将是new_info当时保持的值(也称为副本)
我调用的函数(IIFE - 或立即调用的函数表达式)返回实际的回调。在这个回调中,我没有引用new_info,而是参考IIFE的论点:currentNewInfo

因为每个函数都有自己的作用域,该变量是括起来的(因此名称为 closure ),并且无法访问或更改外。唯一可以访问currentNewInfo变量的是IIFE返回的函数。
也许您担心名称冲突(您创建的每个回调都使用引用currentNewInfo),但事实并非如此:每个回调都是由一个单独的函数创建的,因此可以访问不同的范围。在不能互相访问的范围之间不可能存在名称冲突......只是为了让事情非常简单来理解:

What a closure does

Where  /\            and      /\
       ||                     ||
  is return function()      is scope of IIFE

因此闭包在返回后可以访问函数的作用域。在将表达式解析为值时,该范围具有优先权。为了更好地理解这一点,这里有一个类似的图表,向您展示如何 JS解析表达式:

JS and scope-scanning
每个粉红色的“外部环境记录”是一个函数的范围(已经返回的函数的闭包范围或当前正在调用的函数)。最后一个环境将是全局对象,或者为null(在严格模式下)。这就是它的全部内容。

老实说,首先关闭头部是很棘手的,但是一旦你掌握了我试图在这里解释的内容,它们就会非常有趣。
Check this link我可以继续解释嵌套闭包的用例和好处以及方法,但我最终会写一本书。我发布的链接在解释闭包如何使用相当愚蠢的绘图方面做得很好。它可能看起来很幼稚,但是当我试图掌握lambda函数,闭包和范围的概念时,它们实际上对我有很大的帮助。上面的图表是从我链接到的页面中获取的,它更深入地解释了这些概念,但我仍然认为简单粗略的图纸非常自我解释。

其他问题:
有人指出:“你期待this参考什么。查看代码段,this将仅引用全局对象(window),将相同/类似的事件处理程序附加到window,如果您问我,就没有意义。
全局变量 evil 暗示全局甚至更多。我看不到new_info,也没有myArray在任何地方被宣布。 JS解析表达式的方式有点令人遗憾,并且回归到创建全局变量,而不是窥视:

var bar = 666;//global, always evil
function createGlobal()
{
    var local = 2;
    foo = bar * local;
}
createGlobal();

让我们看一下foo

JS is in createGlobal scope: var local is declared, and assigned 2.
   foo is used, and assigned bar*local
    ||                       ||   \\=>found in current scope, resolves to 2
    ||                       ||
    ||                       \\=>found in global scope, resolves to 666
    ||
    ||
    ||=> JS looks for foo declaration in function scope first, not found
    ||
    ||=> moves up 1 scope (either higher function, or global scope)
    ||
    \\=>Global scope, foo not found, create foo globally! - hence, implied global
             \\
              \\=>foo can now be resolved to global variable, value undefined

过多的DOM查询:您的事件处理程序回调都是这样的:

$('#box2').text(new_info);

这($('#box2'))实际上与写document.getElementById('#box2')相同。这实际上是英语。可以这样考虑一下:每次客户点击$(this) - 无论是什么,您都可以访问DOM,并扫描具有给定ID的元素。为什么不这样做一次并使用内存中保存的引用来更改文本。这节省了无数的DOM查询。你可以使用变量,或者(根据我对闭包的解释),闭包:

var list = (function(box2, list, i)
{//list & i are arguments, so local to scope, too
    for (i = 0; i < myArray.length; i++)
    {
        list += "<br>" + myArray[i].letter;//<-- don't know why you use this
        //new_info = myArray[i].link; no need for this var
        $(this).click((function(new_info)
        {//new_info is closure var now
            return function ()
            {//box2 references DOM element, is kept in memory to reduce DOM querying
                box2.text(link);
            };
        }(myArray[i].link));//instead of new_info, just pass value here
    }
    return list;//return string, assign to outer variable
}($('#box2'), ''));//query dom here, pass reference as argument