JavaScript 设计模式需要帮助:模块的松散增强

8

编辑以使内容更清晰 - @Qantas94Heavy - 我理解它的“意思”或者说它应该做什么,但我不明白的是为什么以及更重要的是如何工作:

我正在阅读一篇关于JS模块模式的高级教程,它给出了以下例子:

var MODULE = (function (my) {
// add capabilities...

return my;
}(MODULE || {}));

我需要你的帮助,因为有一件事情让我感到困扰,就是最后一句话:
(MODULE || {}));

我很难理解这背后的语法规则,想了解更多。用关键词"JavaScript模块语法"和"模块模式简写"进行了一些搜索,但仍然没有完全理解其基础知识。

请问有人能够解释或指点我了解更深入的知识吗?

谢谢, gggi


2
它要么传递模块中已定义的部分,如果不存在,则为模块创建一个新对象 - 函数立即调用,将函数的结果传递给赋值。 - Qantas 94 Heavy
1
@Qantas94Heavy,为什么不把它写成答案而不是评论呢? - Bart
@Qantas94Heavy - 我理解它的“意思”或者说应该做什么,但我不明白的是为什么以及更重要的是如何运作。 - gogogadgetinternet
@Bart:已经超出疲劳的程度,没有足够的时间来证实一个答案。 - Qantas 94 Heavy
3个回答

7
(function(){

})();

这是一个自执行的匿名函数。在您的情况下,它处理"my"对象参数:对"my"进行某些操作,然后将其返回。

在您的情况下,函数接收的"my"参数是"(MODULE || {})"。

&& 和 || 运算符称为短路运算符。|| 将返回 "MODULE" 对象(如果存在),否则将创建一个空对象以在函数内部使用。该函数将对该对象执行其操作,使其成为返回的 "MODULE" 对象。

它通过创建闭包来实现:只要 MODULE 存在(未被垃圾回收),自执行的匿名函数就会连同其赋值时的状态一起存在。这使得添加的任何功能都是持久的。


谢谢@itmitică - 我猜这个解释方式让我认为这里发生了比简单的条件运算符更多的事情!回想起来,这很简单。 - gogogadgetinternet

3
右边被称为立即执行函数。为了理解它的工作原理,让我们将它分解一下:
  1. (...)()我们可以通过函数名来调用一个函数,例如f()。但是,我们可以放置任何解析为函数类型变量的表达式而不是函数名。在我们的例子中,第一组圆括号仅仅包含一个表达式。第二组是函数调用操作符。最终,(f)()恰好等于f()

  2. 第二步是在第一个括号集合内提供一个匿名函数。结果是:(function(){})()。该匿名函数完全属于函数类型。这将导致函数在同一语句中创建、执行和丢弃。

  3. 第二个圆括号集合,也就是函数调用操作符,可以在其内部接受参数,这在我们的情况下是MODULE || {}。该表达式的意思是:如果MODULE被定义,则使用它,否则创建一个新的空对象。

  4. 该参数作为名为my的参数传递给匿名函数,并返回my。这将导致匿名函数求值为my,实际上是:(my)(MODULE || {})

  5. 作用是MODULE是自包含的,并且不会与外部变量发生名称冲突。同时,它可以访问外部变量。

希望这样清楚了 :)

0
很少有大型项目会将所有代码放在一个文件中,一开始将代码放在不同的文件中并按特定顺序组合起来足够容易,但这很快就变得难以管理。使用称为“失去增强”的未知模式,我们实际上可以利用JavaScript异步运行时环境。 为了实现这个模式,我们需要一小段逻辑。 我们要说如果awesomeNewModule存在,则导入它,否则awesomeNewModule只是一个新对象:
    var awesomeNewModule = (function(){
        var exports={
            foo:5,
            bar:10
        };
        exports.helloMars = function(){
            console.log("Hello Mars!");
        };
        exports.goodbye = function(){
            console.log("Goodbye!");
        }
        return exports;
    }(awesomeNewModule || {}));

由于我们使用了 exports 关键字,因此在这里,我们将说 awesomeNewModule 是一个函数内的 exports,现在,如果这是第一个文件,则所有这些值的 exports {foo:5, bar:10} 将被分配给空对象 {},否则将会被分配并扩展已经创建的 awesomeNewModule 如果该文件在模块已经创建后被加载。
var awesomeNewModule = (function(exports){
    var exports={
        foo:5,
        bar:10
    };
    exports.helloMars = function(){
        console.log("Hello Mars!");
    };
    exports.goodbye = function(){
        console.log("Goodbye!");
    }
    return exports;
}(awesomeNewModule || {}));

保持在心中的一件重要事情是,如果awesomeNewModule已经存在,那么您应该确保这些键{foo:5,bar:10}以及这些方法exports.helloMars,exports.goodbye都不存在于awesomeNewModule中。如果是这样,则加载最后的文件将覆盖任何在之前文件中命名相同的方法或值。
因此,您不能跨模块共享值,如果一个模块的任何方面依赖于另一个模块,则无法安全地依赖这些值,但归根结底,编写模块或代码的整个目的是将您的应用程序分成不互相依赖的部分,这样如果一个模块向应用程序引入破坏性错误,则不会影响其他代码的运行。此外,有安全的方法可以确保一个模块不会覆盖来自其他模块创建的模式或方法。

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