3

首先,一个伪代码示例:

;(function(foo){

    foo.init = function(baz) { ... }

    foo.other = function() { ... }

    return foo;

}(window.FOO = window.FOO || {}));

像这样调用:

FOO.init();

我的问题:

  • 什么是技术名称/描述:window.FOO = window.FOO || {}

我了解代码的作用...请参阅下面的询问原因。


问的原因:

我这样调用传入的全局变量:

;(function(foo){
    ... foo vs. FOO, anyone else potentially confused? ...
}(window.FOO = window.FOO || {}));

......但我只是不喜欢称那个小写“ foo”,考虑到全局被称为大写FOO......它看起来很混乱。

如果我知道这种技术的技术名称,我可以说:

;(function(technicalname){
    ... do something with technicalname, not to be confused with FOO ...
}(window.FOO = window.FOO || {}));

我看过一个最近的(很棒的)例子,他们称之为“ exports”:

;(function(exports){
    ...
}(window.Lib = window.Lib || {}));

我想我只是想标准化我的编码约定......我想了解专业人士的工作以及他们的想法(这就是我在这里问的原因)!

4

4 回答 4

4
图案
(function (foo) {
    ...code...
    foo.bar = baz;
    ...more code...
}(window.FOO = window.FOO || {});

您描述的模式没有正式名称,因为它是三个独立的模式组合在一起。每个模式都有多个名称,但在这篇文章中,我将使用以下术语:

  • 关闭
  • 别名
  • 命名空间扩展

关闭

整个模式的基础是closure. 它只是一个用于限定变量和函数范围的函数,这样它们就不会污染全局命名空间:

没有关闭
//these declare window.foo and window.bar respectively
//as such, they pollute the global namespace
var foo;
function bar() {}
闭包,在这种情况下,是立即调用的功能表达式 (IIFE)
(function () {
    //these declare foo and bar within the function
    //but they are not accessible outside the function
    var foo;
    function bar() {}
}());

将变量保存在闭包中的好处是您不必担心有人会覆盖您正在使用的变量。这对于诸如ij经常使用的临时变量尤其重要。

别名

这种模式的第二个重要部分是混叠。别名允许在闭包中定义和使用变量,而无需担心它驻留在哪个全局命名空间中。

无别名
(function () {
    ...
    foo = window.SomeFunction(bar, baz);
    ...
}());
带别名
(function (sf) { //local name
    ...
    foo = sf(bar, baz);
    ...
}(window.SomeFunction)); //global namespace

这一点尤其重要,因为这意味着可以通过在单个位置更改名称来跨大型 JavaScript 文件更改全局命名空间。这是一件好事™。此外,缩小器可以将内部别名缩短为单个字母变量名,例如a,从而在缩小时显着节省字节。

命名空间扩展

命名空间扩展模式依赖于 or 运算符 ( ||) 的合并行为。在许多语言中,&&返回||一个trueor false,但在 JavaScript 中,&&返回第一个falsey值(false, 0, '', null, undefined),然后||返回第一个truthy值(任何不是falsey)。对于这两个运算符,如果未找到相应的类型,则返回最后一个参数。这使得||操作符成为一种方便的方式,仅当它不存在时才定义一个新的命名空间。

没有命名空间扩展
if (typeof window.Foo === 'undefined') {
    window.foo = {};
}
带有命名空间扩展
window.foo = window.foo || {};

这很有用,因为它允许使用其他属性和方法扩展命名空间,而不必担心定义属性和方法的顺序。

在第一个示例中,FileA需要在之前执行FileB

文件A.js
window.foo = {};
window.foo.bar = 'baz';
文件B.js
window.foo.fizz = 'buzz';

在第二个示例中,File1可以File2按任何顺序执行:

文件1.js
window.foo = window.foo || {};
window.foo.bar = 'baz';
文件2.js
window.foo = window.foo || {};
window.foo.fizz = 'buzz';

现在都在一起了

将每种模式一起使用会创建一个非常强大的模块化脚本:

//use foo internally so that you don't have to worry about
//what the global namespace is called
(function (foo) {
    //declare variables internally that you want to keep local to the script
    var i,
        len,
        internal,
        qux;
    //declare functions/properties on the alias when you want to expose them
    foo.bar = function () {...};
//extend the global namespace so that existing extensions are persistent
}(window.FOO = window.FOO || {}));
于 2012-10-08T03:24:00.970 回答
2

我一直将其理解为“Null Coalescing”

至于影响你的 IIFE,window.FOO如果它已经实例化,你就传入,如果没有实例化,你就传入一个空对象。

您也可以将其解读为:

window.FOO = window.FOO || {};
;(function(foo){

    foo.init = function(baz) { ... }

    foo.other = function() { ... }

    return foo;

}(window.FOO));

就个人而言,我更喜欢不同的模式:

var FOO;
if (!FOO) {
    FOO = {};
}
(function () {
    "use strict";
    FOO.prop1 = 'bar';
    FOO.bar = function (z) {
        return z + 1;
    };
}());

我发现它不那么令人困惑,并帮助我确保干净的命名空间。

于 2012-10-08T03:28:28.847 回答
2

为了补充皮特的答案,另一种形式:

;(function() {
  var Foo = window.Foo = window.Foo || {};

  Foo.foo  = 'bar';
  Foo.baz  = function() {
    return "Hello World!";
  };
})();

我通常使用 localvar来给 minifier 节省几个字节的机会。当您在命名空间深处的多个级别(例如 on )上工作时,这会产生更大的影响var MyView = window.MyApp.Views.MyView

于 2012-10-08T03:36:15.680 回答
1

正如其他人所指出的,您的第一个问题完全独立于您的第二个问题。除了您决定将它们组合在一个语句中而不是在两个语句中这样做之外,它们彼此没有任何关系。

对于您的第一个问题,Douglas Crockford 将其称为default assignment。如,如果变量存在,则不理会它,否则将其初始化为指定的默认值。当您看到如下内容时:

foo = foo || {};

你的大脑应该将其解读为:

foo defaults to `{}`

从技术上讲,它实际上是“如果 foo 是虚假的,则分配{}给 foo”,但我们假设任何虚假的东西,例如零、空或未定义,foo在我们使用它时都是无效值。此外,这个词default已经暗示“如果 foo 没有定义”。

但是,话虽如此,并且知道您的第二个问题,但很明显default作为传递给您的 IIFE 的参数名称并不是一个合适的词。该参数所做的只是将方法和属性附加到传入的对象。在这种情况下,导出是合适的,如“这些是对象的公共导出成员”。我认为附加也是一个合适的名称,如“将这些东西附加到对象”中。我个人的偏好只是简单地将其命名为obj为 in object,这是您希望传入的东西的类型。

于 2012-10-08T03:59:29.833 回答