2

我知道我可以像这样创建一个自调用的嵌套函数

(function ns(){

    (function Class(){

        alert('ns.Class fired');

    })();

})();​

但它很丑,而且看起来不太对劲。

我的印章不是应有的样子,我希望有人可以向我展示一种更好的方法来构建我的命名空间应用程序,并且仍然能够利用“一些”自调用函数。

// THIS DOESN'T WORK
//     because I haven't called "ns"
function ns(){

    (function Class(){

        alert('fired');

    })();

};​

出于我的目的,我问这个的原因是为了更好地命名我的 JS 和 jQuery。

所以我希望能够做这样的事情(这确实有效)

var ns = ns || {};
ns.Class = function(){};

ns.Class.Navigation = (function() {

    $('#element').on('click', function() {alert('element clicked');});

})();​

但我不确定这是否是构建更大(阅读:js 重)应用程序的正确方法?!?!

  • 这个重量是不是太重了?
  • 有没有更聪明的方法来实现这一目标?
4

4 回答 4

4

好吧,首先:您调用“主要”IIFE 的事实ns表明您将其视为命名空间对象,这并不完全正确。命名空间通常使用 IIFE 创建,因为它们具有闭包作用域的额外好处。但归根结底,名称空间只是 Object(文字)的一个花哨的词。
使用最流行的 lib/toolkit/framework:jQuery。基本上,它是一个巨大的 IIFE,它构造了一个同样巨大的对象,它被分配了许多方法和属性(好吧,无论如何都是对函数对象的引用)。在该 IIFE 中创建了一些其他对象和变量,但它们要么根本不暴露给全局对象,要么(非常)间接地暴露给全局对象。

请允许我澄清一下:

var myNameSpace = (function()
{
    var invisibleVar = 'can\'t touch this';
    var objectLiteral = {sing: function()
        {
            return invisibleVar;//exposed, albeit indirectly
        },
        property: 'Hammertime'
    };
    var semiExposed;
    objectLiteral.getSemi = function(newVal)
    {
        return semiExposed;//change closure var
    };
    objectLiteral.changeSemi = function(newVal)
    {
        semiExposed = newVal;//change closure var
    };
    objectLiteral.restoreSemi = (function(initVal)
    {
        return function()
        {
            semiExposed = initVal;//restore to value set when main IIFE was executed
            //don't worry about what this references: use the force... of the scope
            return objectLiteral.getSemi();//<-- return init val
        };
    }(semiExposed));//pass initial value to this scope
    var notExposedAtAll = function()
    {//can't be called but inside the main IIFE scope (and subsequent scopes)
        objectLiteral.foo = 'But it adds a public property';
    };
    objectLiteral.changeMe = function()
    {
        notExposedAtAll();//called in default context (either null or global, but it doesn't matter here)
    };
    return objectLiteral;//direct exposure
}());

这使用了所有工具包/库的一些基本原则,实际上所有体面的 JS 脚本共享:使用函数作为第一类对象,使用它们作为表达式来创建临时范围等......)
IMO,它IIFE提供了一个很好的案例:作用域为您提供了足够的时间将任何对象分配给变量,因此无论您如何创建方法(有或没有 IIFE),您都不必担心this在任何给定时间引用什么,只需使用变量。
您也可以实现一些基本的数据隐藏。在此示例中,初始值semiExposed被传递给 IIFE,并保留在其范围内。没有什么可以解决这个问题(嗯,目前还不是很正确),所以你总是可以恢复到任何属性的初始值。

然而,我承认,随着代码的增长,IIFE 会让你的代码更难阅读,我完全理解你为什么不想过多地使用它们。您可以研究一下bind,它会帮助您减少许多 IIFE,但也有不利的一面。例如,有些人仍然使用 IE8,它不支持bind.
但另一个选择是:创建一个简单的 IIFE工厂函数:

function giveScope(varsFromScope,toFunction)
{
    return function()
    {
        var passArguments = Array.prototype.slice.apply(arguments,[0]);//get args from call
        passArguments.push({scope:varsFromScope});
        toFunction.apply(this,passArguments);
    };
}
var pseudoClosure = giveScope({scopeContext: this, something:'else'},function(arg1,arg2)
    {
        //function body here
        arguments[arguments.length - 1].currentContext;//<== "closure scope"
        this;//called context
    });

这样,您可以摆脱一些 IIFE,通过将它们替换为传递对象的简单函数调用。简单,并且兼容 X 浏览器。

最后,您的第一个片段是我倾向于在事件委托中使用的内容:

var target = e.target || e.srcElement;
var parentDiv = (function(targetRef)
{
    while(targetRef.tagName.toLowerCase() !== 'div')
    {
        targetRef = targetRef.parentNode;
    }
    return targetRef;
}(target));

这样,我不必在同一范围内创建另一个可验证对象,我的 targetRefparentDiv在找到 div 时被分配,并且 targetRef 被 GC'ed,我完成了它,所以不需要那个变量留在范围内。

现在已经很晚了,我不知道我是否说得很有道理。底线是:你可能讨厌IIFE,但你真的不能没有它们。
如果是大量括号困扰您,您可能会很高兴知道您不必使用它们。任何强制 JS 引擎将函数声明解释为表达式的运算符都可以:

(function()
{
}());
//can be written as:
!function()
{
}();
//or
~function()
{
}();
//or when assigning the return value, you don't even need anything at all:
var foo = function()
{
    return 'bar';
}();
console.log(foo);//logs bar

也许您更喜欢另一种表示法?但老实说:您可能不喜欢这种语法,但恐怕您将不得不忍受它,或者切换到咖啡脚本或其他东西。

于 2012-12-04T17:04:17.520 回答
1

我不太清楚你的目标是什么。你只是在寻找这样的东西吗?:

var ns = ns || {};
ns.Class = {
    Navigation: (function() {
        return $('#element').on('click', function() {alert('element clicked');});
    })()
};

此处处理程序已附加,并且ns.Class.Navigation是对 jQuery 包装器的引用#element。如果您不需要该引用,那么为什么要为调用立即调用的函数表达式的(否则为空的)结果分配任何内容? ns.Class不是一个函数,但看起来你实际上并没有将它用作一个函数。

这是否符合您的要求?

于 2012-12-04T16:47:45.770 回答
0
$(function ns() {
    "use strict";
    (function Class(){
        alert('ns.Class fired');
    }());
});

ns 被传递给 jquery 对象,并且调用发生在函数括号内 - 通过 crockford 测试(jslint)。

于 2012-12-04T16:36:38.030 回答
0
const outerName = (function innerFunc() {
  // Private stuff executed once on creation.
  const createdOnce = document.createElement('textarea');

  return function returnedFunc(someArg1, someArg2) {
    console.log('Hi!');
  };
}());
于 2017-07-25T10:54:34.793 回答