JavaScript匿名构造函数:!function(){}();

5

我看到过用于在JavaScript中创建匿名构造函数的语法:

var Application = Application || {};
!function(window, Application) {
   Application.property = {/*...*/}
   Application.method = function(){/*...*/}
}(window, Application);

我希望你能理解以下部分的含义:
  1. 使用第一行(即 var o = o || {};)的优点是什么,相比之下直接声明 var o = (function(){})(); 有何区别?
  2. 为什么在函数前面要使用 !
  3. 既然 windowApplication 是全局对象,为什么要把它们作为参数传递?
  4. 这是否是匿名构造函数最方便的方式,它为什么比以下方式更好:

4a)

 var Application = { 
    property: {},
    method: function(){}
 }

或者4b)
 var Application = (function() {
    var method = function(){/*...*/}
    return {method:method};
 }());

它并不比你的替代代码更好,我会说它更糟糕。 - akonsu
这很有趣,但为什么? - Vad
这两个代码片段并不等价。!function(){}() 代替了 (function(){})(),详见此问题 - Vatev
优点是,如果已经存在一个应用程序实例,你不会用新实例覆盖它。 - net.uk.sweet
3个回答

2
第一行代码的目的是确保Application对象一直存在,通常用于在预期Application对象已经存在的情况下,函数只是增强现有对象。如果不存在,则确保我们不会因为访问未定义属性而出现错误。你的例子只在Application对象尚不存在的情况下等效。在所有其他情况下,你的代码将覆盖现有的Application对象,这几乎肯定不是意图所在。
来自Vatev的注释解释了!的作用。它是另一种语法,使得正在讨论的函数成为自执行匿名函数。(顺便说一下,它还接受函数的返回值 - 目前是undefined,并翻转其真实性,使其计算为true。但由于结果没有存储在任何变量中,显然不是目的。)
最后,为什么要将windowApplication传递到函数中并在其中使用?这是一个安全功能,以防其他代码稍后更改windowApplication。它保证在匿名函数内,windowApplication正是你期望的那样。在你给出的简写示例中,这似乎无关紧要 - 毕竟,如果你立即使用并不存储它们,为什么要保护这些变量呢?在许多情况下,你从此函数返回一些东西,然后windowApplication将被存储在闭包中,因此你将保留这些变量。它使其免受那些后来决定说Application = {...}的人的攻击。

“安全原因”并不是一个充分的理由:如果值被覆盖了,它就被覆盖了-没有别的。唯一可以保护自己的值是通过将其指定为参数中的“undefined”,但不传递任何内容来保护自己(仅适用于允许覆盖undefined的旧浏览器)。除此之外,别名全局对象对于代码压缩非常有用。 - Ingo Bürk
1
如果您将Application传递给函数并将其存储在闭包中,而在闭包外部,将Application =“hello”,则在闭包内部,Application仍将指向原始对象。 - Scott Mermelstein
是的,但如果在调用IIFE之前调用了Application =“Foo”,则外部代码仍然可以覆盖它。诚然,这是一个原因,但它并不是万无一失的。我想我没有表达清楚,抱歉。 - Ingo Bürk

0

由于目前为止两个答案都忽略了这两个细节:像jQuery这样的库使用的模式是使用以下两个参数:

(function (window, undefined) {
    window.myPlugin = ...;
})(window);

这里有两件事情:

  • 指定了undefined作为参数,但没有传入。这样,在内部范围内保证了undefined具有预期的值。这仅对允许覆盖undefined的旧浏览器是必要的(尽管一直被认为是不好的做法 - 这只是像jQuery这样的库避免其他代码干扰其代码所做的)。

  • 在范围内别名化全局对象,如window,可以更小地压缩代码(对于undefined也适用)。显然,您对别名化对象的引用越多,您将节省的就越多:

.

(function(w,u){w.myPlugin=...;w.somethingElse=...;if(whatever===u){return;}})(window);

相比之下

(function(){window.myPlugin=...;window.somethingElse=...;if(whatever===undefined){return;}})();

window 通常不会节省太多空间,因为你通常不想在全局对象中混杂其他东西。但是给 undefined 取个别名可以节省相当多的空间。


0

我不知道所有的答案,但我会尽力回答我所知道的。

  1. 使用 var Application = Application || {}; 只是意味着你正在处理作用域。如果 "Application" 变量已被定义,它只会意味着它继承并现在可用于当前作用域。如果没有,则 "||" 部分(表示或)意味着它将被创建为空对象。你正在处理创建一个对象然后对其进行操作,而不仅仅是返回原始结果。这就是为什么你不应该使用 "o = (function() {...});"。"o" 将成为函数的结果而不是对象。

  2. 使用 "!function" 使其被视为表达式,但这可能很棘手,因为你可能必须考虑相反的情况。例如,!null 意味着你正在检查它不是 null,而不是你正在检查它是否为 null。

  3. 传递窗口和应用程序对象处理作用域,同样需要显式地将它们传递到函数中,然后在最后返回它们。把衣服放进洗衣机里就像这个过程。你把它们放进去,然后发生一些事情,最后你再把它们拿出来。这是一个非常粗糙的想法,并不是最好的例子,但我几年前是这样想的。你把衣服放进去,然后调用漂洗函数、肥皂函数、再次漂洗,然后甩干除水。

  4. 希望其他人能回答这个问题,我不确定区别是什么。


3.这也经常被做,因为它将那些对象别名化,从而允许缩短它们,从而在缩小时节省一些位和字节。 - Ingo Bürk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接