好吧,首先:您调用“主要”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
也许您更喜欢另一种表示法?但老实说:您可能不喜欢这种语法,但恐怕您将不得不忍受它,或者切换到咖啡脚本或其他东西。