为什么这里似乎需要分号?

时间:2017-01-28 01:47:22

标签: javascript syntax webpack

在我的Webpack捆绑的JS应用程序的矿井深处,我找到了这段代码:

var headers = Object.keys(headersObj).map(function (name) {
	return [headersObj[name].name, headersObj[name].value]
})
	
(window).fetch(self._opts.url, // and so on...

这似乎来自于Slack API节点库(在某种程度上)所需的stream-http。

此代码在运行时会抛出此错误:

VM481:672 Uncaught TypeError: Object.keys(...).map(...) is not a function
    at module.exports.ClientRequest._onFinish (eval at App (container.js:94), <anonymous>:672:4)
    at module.exports.eval (eval at App (container.js:94), <anonymous>:614:9)
    at module.exports.EventEmitter.emit (eval at App (container.js:94), <anonymous>:3615:18)
    at finishMaybe (eval at App (container.js:94), <anonymous>:4371:15)
    at afterWrite (eval at App (container.js:94), <anonymous>:4253:4)
    at afterTick (eval at App (container.js:94), <anonymous>:4719:11)
    at Item.run (eval at App (container.js:94), <anonymous>:3037:15)
    at drainQueue (eval at App (container.js:94), <anonymous>:3007:43)

如果在未定义的变量上运行Object.keys,则会发生同样的错误。但是,headersObj已定义,并且是一个对象。

当我将代码更改为:

var headers = Object.keys(headersObj).map(function (name) {
	return [headersObj[name].name, headersObj[name].value]
}); // <- please note yon semicolon
	
(window).fetch(self._opts.url, // and so on...

正常运行。问题是,这不是我的代码。我无法轻易地在捆绑脚本的上游添加分号。

  1. 为什么这个分号似乎是必要的?
  2. 如何在不更改源代码的情况下解决此问题?
  3. 修改

    看起来问题(在评论中指出)是原始代码被解释为:

    var headers = Object.keys(headersObj).map(function (name) {
    	return [headersObj[name].name, headersObj[name].value]
    })(window).fetch(self._opts.url, // and so on...

    更明显地是对window传入的结果(列表,而不是函数)的调用。

    window的实例由Webpack插件生成:

    plugins: [
      new webpack.DefinePlugin({
        global: 'window'
      })
    ],

    似乎隐含地将window包裹在parens中。有没有办法改变这种行为?

2 个答案:

答案 0 :(得分:2)

<强> 1。为什么这个分号似乎是必要的?

如果你不添加分号,JavaScript会理解它需要获取表达式var headers = Object.keys(headersObj).map(...)的返回值 - 这是一个数组 - 然后调用它,传递window as一个参数。它会抛出一个错误,因为你的数组不是一个函数。它基本上是这样做的:

var headers = (Object.keys(headersObj).map(...))(window).fetch(self._opts.url, // and so on...

<强> 2。如何在不更改源代码的情况下解决此问题?

如果不更改源代码,我无法找到解决方法。您可以在第一行的末尾(如您所指向的那样)或第二行的开头添加分号:

var headers = Object.keys(headersObj).map(...)

;(window).fetch(self._opts.url, // and so on...

文章Semicolons in JavaScript are optional描述了您在“没有分号编码时唯一真正的陷阱”一节中遇到的同样问题。

答案 1 :(得分:0)

臃肿??

这是一个奇怪的情况,因为你不希望看到单个表达式包裹括号。 (window)window相同,但括号是多余的。

显而易见的解决方案是简单地删除多余的括号

var headers = Object.keys(headersObj).map(...)

window.fetch(self._opts.url, // and so on...

添加一个领先的分号只是膨胀。

立即调用的匿名函数

存在立即调用的匿名函数

的情况
(function(){... some code }());  // the Crockford way or
(function(){... some code })(); 

脚本文件的开头行多少次。如果将这样的脚本文件连接成单个文件以进行发布,如果前面的行没有分号并且作为表达式求值,则会抛出相同的错误。

var a = 0       // this line is an expression (it has a value)
(function(){... some code }())  // throws 0 is not a function

if(a !== b){ }  // this line is a statement it does not have a value
(function(){... some code }())  // works

如果您编写公共库或连接自己的库文件,在使用立即调用的匿名函数时,它是第一个表达式,它需要在第一个括号之前放置一个分号以防止挂起表达式(因为想要更好术语)

var a = 0 // the expression stays in context until terminated
;(function(){... some code }())  // safe as the previous expression is no longer in context. 

另请注意,立即调用的匿名函数将作为表达式进行求值。

(function(){... some code }())
(function(){... some code }())  // throws "is not a function error..

(function(){... some code })()  // alternative form
(function(){... some code }())  // throws "is not a function error... 

因此,使用分号明确终止表达式总是好的做法。

// good if guaranteed first line
(function(){... some code }());
(function(){... some code }()); 

// Best
;(function(){... some code }());
(function(){... some code }());