如何在命名空间应用程序的大图中正确构建自调用嵌套函数?

时间:2012-12-04 16:05:36

标签: javascript jquery

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

(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 个答案:

答案 0 :(得分: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));

这样,我不必在同一范围内创建另一个veriable,当找到div时我的targetRef被分配给parentDiv,而targetRef是GC,我已经完成了,所以不需要将变量留在范围内。

现在已经相当晚了,我不知道我是否有多大意义。底线是:你可能讨厌 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

也许您更喜欢其他符号?但老实说:你可能不会喜欢语法,但我担心你不得不忍受它,或者改用coffeescript或其他东西。

答案 1 :(得分:1)

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

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

这里附加了处理程序,ns.Class.Navigation是对#element周围的jQuery包装器的引用。如果您不需要该引用,那么为什么要为调用立即调用的函数表达式的(否则为空)结果赋值? ns.Class不是一个函数,但它看起来并不像是将它实际用作函数。

这是否符合您的要求?

答案 2 :(得分:0)

$(function ns() {
    "use strict";
    (function Class(){
        alert('ns.Class fired');
    }());
});

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

答案 3 :(得分:0)

const outerName = (function innerFunc() {
  // Private stuff executed once on creation.
  const createdOnce = document.createElement('textarea');

  return function returnedFunc(someArg1, someArg2) {
    console.log('Hi!');
  };
}());