高级 JavaScript 初始化

Advanced Javascript initialisation

本文关键字:初始化 JavaScript 高级      更新时间:2023-09-26

我在破译以下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));

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

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

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

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

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

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

例如,jQuery使用$来做几乎所有事情。大多数人喜欢使用$('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.nav缩短到nav。这可能是最好的方法。

替代方法(不推荐(:

// 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 属性会稍微快一点。(这确实是一种微优化,但在这种情况下,出于不同的原因[为了清楚起见],它是方便合理的。

这是一个逐行的解释(标题只是为了分解它(:

设置

// 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.
})();