3

我无法破译以下 Javascript 初始化语句:

(function(NAMESPACE) {
        NAMESPACE.nav = {};
        var nav = NAMESPACE.nav,

_init = false,
        _isNavOpen = false,
        _inner = document.getElementById('inner-wrap');

    // constants
    nav.CLASS = 'js-nav-open';
    nav.CLASS_READY = 'js-nav';
    nav.CONTAINER = '#nav';
    nav.DURATION = 400;
    nav.HAS_CSSTRANSITIONS = $('html').hasClass('csstransitions') && $('html').hasClass('csstransforms3d');

……

// toggle open/close
    nav.toggle = function(event) {
        event.stopPropagation();

        if(_isNavOpen && $('html').hasClass(nav.CLASS)) {
            nav.close();
        } else {
            nav.open();
        }

        // this is for the links
        if(event) {
            event.preventDefault();
        }
    };

}(PROJECT_NAME));

这似乎不必要地复杂 - 在 2 行中调用(或设置?)“导航”3 次。有人可以解释一下像这样翻转它有什么意义吗?

4

3 回答 3

4

这是一个 JavaScript 闭包的示例,它通常用于创建私有范围并避免对象污染全局范围。

以这种方式创建插件是很常见的,以避免由于具有相同名称的变量等而与页面上的其他功能发生冲突。本质上它是一种管理范围的机制。

于 2013-10-01T00:51:45.670 回答
4

这是使用 jQuery 时的常见做法:

(function ($) {
    var div = $('#my-div');
    // Etc
}(jQuery));

将脚本包装在闭包中可确保某些变量具有您期望的值。

例如,jQuery 使用$for 做几乎所有事情。大多数人喜欢使用$('do something')而不是jQuery('do something').

但是假设页面上有另一个库也使用全局变量$

通过将代码包装在闭包中,您将“保留”$为 jQuery,并且单独使用 jQuery。(当你jQuery作为参数传入闭包时,在这个函数的范围内只能$表示“jQuery”。)


同样,在您的示例中,您正在保留NAMESPACE变量。即使有另一个名为 的变量NAMESPACE,在页面上的其他地方引起了骚动,通过在闭包结束时传入一个变量,您也可以保证这NAMESPACE将是您期望的对象(至少在闭包内) .

假设您有一个名为 的全局变量AbominableSnowman,但您想AS用作快捷方式。通过做这个:

var AS = "Apple Soup";

(function (AS) {
    AS.tellMeAboutSnowmen();
    alert(AS.snowballs);
}(AbominableSnowman));

您的代码仍将按您的预期运行。(证明:http: //jsfiddle.net/RUzZH/1/


至于“翻转它”,似乎最初的程序员想缩短NAMESPACE.navnav. 这可能是最好的方法。

另一种选择(不推荐):

// It's best to limit your assignments to 1-per-line
// This kind of code isn't fun to debug, or even read
var nav = NAMESPACE.nav = {};

这似乎不是什么值得担心的事情。NAMESPACE.nav但是,由于此脚本经常交互,因此直接使用变量引用.nav属性会稍微快一点。nav(这确实是一个微优化,但在这种情况下,出于不同的原因 [为了清楚起见],它是合理的。

于 2013-10-01T01:09:03.490 回答
3

这是逐行解释(带有标题只是为了将其分解):

设置

// Create an anonymous function expression taking `NAMESPACE` as a parameter.
// Likely the *real* namespace will be passed to the function at the end
// with ... })(realnamespacetomodify);
(function(NAMESPACE) {

// Create the new part of the namespace.  Note that we are editing a reference
// so really this change happens on whatever object was passed in.
    NAMESPACE.nav = {};

// Create a local pointing to this new sub-namespace.  Probably just for
// convenience, also possibly for portability (if the name is used in closures,
// then those closures don't need to refer to NAMESPACE directly).
    var nav = NAMESPACE.nav,

模块定义

// While nav refers to an object likely in global scope, nav itself can
// never be referred to from global scope because it is a local here.

// These variables are local here.  They can never be referred to by global scope.
    _isNavOpen = false,
    _inner = document.getElementById('inner-wrap');

// These variables, added to nav, can be accessed using the object that
// nav refers to in global scope (see the end).
    nav.CLASS = 'js-nav-open';
    ... 

// This function is also added to nav, therefore it can be accessed outside
    nav.toggle = function(event) {
        ...

        // This reference to _isNavOpen resolves because this function
        // is a closure, and binds variables outside its scope
        // to the function itself.  So even though _isNavOpen can't be
        // accessed globally, it can be accessed here, making it like
        // a private member of this namespace.
        if(_isNavOpen && $('html').hasClass(nav.CLASS)) {
            // nav is also bound by the closure and can be accessed here
            nav.close();
        } ...
    };

在全局空间中使用

}(PROJECT_NAME));

console.log(PROJECT_NAME.nav.CLASS); // "js-nav-open"
console.log(PROJECT_NAME.nav.toggle); // Function object

这是一个模块模式。使用它有几个原因:

  • 代码可移植性(不指模块内的全局对象)
  • 作用域(避免将不必要的变量分配给全局命名空间)
  • 可见性(隐藏私有访问变量)

至于前三行本身(您的原始问题),他们可以直接引用PROJECT_NAME,但看起来它已被设置为帮助代码可移植性。您会注意到匿名函数本身从不指向真实对象 ( PROJECT_NAME)。这意味着您可以复制和粘贴此部分,并且只能在一个地方更改该引用。

另一个答案提到了范围,虽然这也很重要,但它并没有解释这段代码的所有好处,例如为什么它不直接引用现有的全局变量。使用模式的这一部分可以实现范围隐藏的好处:

(function() {
  ... // Anything set here is local, not global.

})();
于 2013-10-01T00:59:37.113 回答