当我在Node JS中调用modernizr.build时,非常不寻常的范围行为

时间:2016-01-13 17:49:23

标签: javascript node.js modernizr

我正在尝试使用Modernizr同时构建几个jsons,但它似乎打破了我的功能范围。 这很难解释所以看看这个例子,如果你不相信我就试试吧:

[1,2,3,4,5].forEach(function(i){
    require("modernizr").build({}, function (result) {
        console.log(i);
    });
})

输出:

5
5
5
5
5

而不是预期的1,2,3,4,5,与任何类似的函数一样。

在我使用ECMAScript语言编写的所有年份之前,我没有遇到过这种行为,并且已经构建了我的项目(以及之前的项目)围绕着这样的想法,即你不能破坏这样的函数范围。

它基于承诺或甚至简单的回调打破任何系统。 这一天让我感到困惑,我无法找到合适的解决办法。 我甚至很难概念化导致这种情况发生的原因。 请帮忙。

编辑:

好的,看起来你们都挂在了forEach上...... 这是另一个让它更清晰的例子:

function asd(i){
    require("modernizr").build({}, function (result) {
        console.log(i);
    });
}

asd(1);
asd(2);
asd(3);
asd(4);

输出

4
4
4
4

究竟发生了什么?

2 个答案:

答案 0 :(得分:0)

函数块是异步调用的,所以这种行为是预料之中的,因为这个调用比foreach的行走慢得多,所以当你到达function (result) {}块时i已经是5个

Node.JS: How to pass variables to asynchronous callbacks?中描述的问题完全相同,您应该可以使用相同的解决方案

[1,2,3,4,5].forEach(function(i){
    (function(i) {
        require("modernizr").build({}, function (result) {
            console.log(i);
        });
    })(i);
})

未经测试但有些像这样的工作

答案 1 :(得分:0)

Modernizr特有的问题不得不对全局变量进行破坏。

build命令基本上是一个大的requirejs配置函数,全部由大型配置对象提供支持。总有一些基本的东西是在函数顶部建立的

{
  optimize: 'none',
  generateSourceMaps: false,
  optimizeCss: 'none',
  useStrict: true,
  include: ['modernizr-init'],
  fileExclusionRegExp: /^(.git|node_modules|modulizr|media|test)$/,
  wrap: {
    start: '\n;(function(window, document, undefined){',
    end: '})(window, document);'
  }
}

然后,由于Modernizr在浏览器和节点中都可以不做任何更改,因此需要有一种方法让它知道它是应该通过文件系统还是通过http加载它的依赖项。所以我们在环境检查中添加了一些像basePath这样的选项

if (inBrowser) {
  baseRequireConfig.baseUrl = '/i/js/modernizr-git/src';
} else {
  baseRequireConfig.baseUrl = __dirname + '/../src';  
}

此时,配置对象被传递到requirejs.config,这需要连线,并允许我们开始调用build

最后,在创建了所有这些之后,我们还有一个构建函数,它还会再次修改配置对象以进行构建特定设置(构建中的实际检测,正则表达式以消除某些AMD crud等)

所以这是一个超级简化的伪代码版本,最终发生了什么

var config = {
  name: 'modernizr'
}

if (inBrowser) {
  config.env = 'browser';
} else {
  config.env = 'node';    
}

requirejs.config(config);

module.exports = function(config, callback) {
  config.out = function (output) {
    //code to strip out AMD ceremony, add classPrefix, version, etc

    callback(output)
  }

  requirejs.optimize(config)
}

发现问题?

因为在运行异步require.optimize函数之前,我们正在触及配置对象的.out方法(其范围是整个模块,因此它的上下文在build()调用之间保存) ,每次调用.out时,您传递的回调都会重写build方法。

这应该在Modernizr中用几个小时修复