Javascript 变量范围和对象作为参数传递给函数

Javascript variable scope and object passed as argument to function

我正在使用 Laravel Elixir 和 Laravel-Elixir-SpriteSmith 创建一些精灵图标。我至少有 3 个不同的图标大小存储在不同的文件夹中,所以我尝试遍历这些文件夹并执行为每个文件夹生成精灵的任务:

我的问题是,在传递给 mix.spritesmith 的第二个参数的 cssOpts.cssSelector 函数中,set 的值始终是最后一个值。

代码解释得更好:


var sources = [
        { size: 16, source: 'resources/bower_components/famfamfam-silk/dist/png' },
        { size: 22, source: 'resources/assets/icons/22' },
        { size: 38, source: 'resources/assets/icons/38' }
    ];

    for(var i in sources) {
        var set = sources[i];

        mix.spritesmith(set.source, {
            imgOutput: 'public/icons',
            cssOutput: 'public/css',
            imgName: set.size + '.png',
            cssName: 'icons-' + set.size + '.css',
            imgPath: '../../icons/' + set.size + '.png',
            cssOpts: {
                cssSelector: function(item) {
                    console.log(set.size); //PROBLEM HERE. Always prints 38! Should print 16 - 22 - 38.
                    return '.icon' + set.size + '-' + item.name.replace('_', '-');
                }
            }
        });
    }

我应该怎么做才能在每个循环中将正确的值传递给函数?

您遇到的问题称为后期绑定。您可以在多个 SO 问题上阅读它,例如:Late binding onclick event

你基本上需要创建一个新的作用域,它会绑定一个 set 的特定值。

cssSelector: (function(innerSet) {
    return function(item) {
        console.log(innerSet.size);
        return '.icon' + innerSet.size + '-' + item.name.replace('_', '-');
    };
})(set)

我同意其他人的看法,这是由于闭包和变量提升在 javascript 中的工作方式:var set 是上层函数作用域的一部分,而不是循环。因此,在执行 cssSelector 时,set 等于数组中的最后一个元素。

您可以使用@ralh 的方式,或者使用 forEach 方法:

var sources = [{
    size: 16,
    source: 'resources/bower_components/famfamfam-silk/dist/png'
}, {
    size: 22,
    source: 'resources/assets/icons/22'
}, {
    size: 38,
    source: 'resources/assets/icons/38'
}];

sources.forEach(function (set) {
    mix.spritesmith(set.source, {
        imgOutput: 'public/icons',
        cssOutput: 'public/css',
        imgName: set.size + '.png',
        cssName: 'icons-' + set.size + '.css',
        imgPath: '../../icons/' + set.size + '.png',
        cssOpts: {
            cssSelector: function(item) {
                console.log(set.size); //PROBLEM HERE. Always prints 38! Should print 16 - 22 - 38.
                return '.icon' + set.size + '-' + item.name.replace('_', '-');
            }
        }
    });
})