我已经在JS编程了一段时间,但我从未想过要使用立即函数,例如:
(function(){
console.log('hello, I am an immediate function');
}())
如果我刚刚写下来会有什么不同:
console.log('hello, I am an immediate function');
?我无论如何都无法访问此功能(它没有分配到任何地方)。我认为(但我不确定)我可以在没有直接功能的情况下实现所有功能 - 那么为什么人们会使用它呢?
答案 0 :(得分:4)
Immediately invoked function expressions用于创建namespaces。我不会准确解释为什么IIFE如此重要 - 本·阿尔曼在这方面做得非常好。不过,我会说IIFE最重要的用途之一是创建闭包。考虑:
var counter = (function () {
var count = 0;
return function () {
return ++count;
};
}());
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
我们使用IIFE的原因是因为JavaScript没有块范围。因此,IIFE用于模拟块范围。 JavaScript 1.7引入了let
关键字,允许您创建块范围变量。但是大多数实现都不支持let
,因此我们仍然使用IIFE。
例如,可以使用let
关键字重写上述程序,如下所示:
var counter;
{
let count = 0;
counter = function () {
++count;
};
}
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
另请注意,IIFE可以返回值。普通块不能这样做。因此,您需要手动将返回值分配给全局变量。
然而,大多数情况下,IIFE用于封装库的私有状态。创建一堆全局变量被认为是不好的做法。
有时它用于封装逻辑上相似的元素,因为我们喜欢将人物分组。它可能没有什么区别,但组织代码是好的。例如,考虑:
function Pet(name) {
this.name = name;
}
Pet.prototype.feed = function (food) {
console.log(this.name + " is eating " + food + ".");
};
而不是这样,只需将代码封装起来如下:
var Pet = (function () {
function Pet(name) {
this.name = name;
}
Pet.prototype.feed = function (food) {
console.log(this.name + " is eating " + food + ".");
};
return Pet;
}());
哦,最重要的。 IIFE也用于解决Javascript infamous Loop issue?
答案 1 :(得分:4)
更新
我找到了this question,我在那里详细介绍了闭包,完成了一些小图,以阐明闭包和功能范围是如何使用的(以及它们如何帮助你)。
IIFE是函数对象,是在JS中创建真实范围的唯一方法。它们被用在了各处。你曾经使用过jQuery或其他一些lib吗?然后你使用了使用IIFE的代码。
曾经看过node.js?然后你可能会遇到一些名为“模块模式”的东西
如果您已经编写了自己的构造函数,那么您可能想知道如何在对象上使用某种私有属性。答案是IIFE一次又一次。
另外:DOM api不是世界上最快的东西。拥有一个包含硬编码document.getElementById
调用的函数意味着,每次调用该函数时,都将遍历DOM。那不太理想。解决此问题的快速而简单的方法是:
var f = (function(domReference)
{
var visible = !!(domReference.style.visibility === 'block')
return function()
{//has access to vars and arguments of outer function
domReference.style.visibility = visible ? 'none' : 'block';
visible = !visible;
}
}(document.getElementById('foobar'));
但也许最好的,最常见的例子是循环中的超时/间隔:
for (var i=0;i<10;i++)
{
setTimeout((function(currentI)
{
return function()
{
console.log(currentI);
console.log(i);
}
}(i)), 1000);
此处,currentI
将记录0,1,2 ......依此类推,而i
将始终记录10
。超时回调的原因不是获得i
值的副本,而是引用变量i
。该变量可以根据需要更改其值,当调用超时回调时,它将记录上次分配的值i
。在定义回调时,未分配值i
。
没有详细介绍,我提到了对象的私有属性。好吧,这里有一个例子可以帮助你弄清楚为什么IIFE和功能范围是至关重要的,以及JS的强大功能:
var MyConstructor = (function()
{
return function(foo, bar)
{
var initState = Array.prototype.slice.apply(arguments, [0]);
this.getInit = function()
{
return initState;
};
this.foo = foo;
this.bar = bar;
}
}());
(function(constant)
{
var F = function(args)
{
return MyConstructor.apply(this, args);
},
checkVar = constant || 'default constant value';
F.prototype = MyConstructor.prototype;
MyConstructor.prototype.getConstant = function()
{
if (constant !== checkVar)
{//bad example
return checkVar;
}
return constant;
};
MyConstructor.prototype.regularMethod = function()
{
return this.foo;
};
MyConstructor.prototype.copyFromInitState = function()
{
return new F(this.getInit());
};
}('Immutable value'));
玩得开心......:P
答案 2 :(得分:1)
开发人员使用立即函数声明私有作用域。例如......
(function($){
$("div").html("example");
})(jQuery);
使用此代码,您不需要全局范围内的$
变量,只需外部window.jQuery
。当你想使用另一个库在window
对象中使用的变量名时,它可以防止可能的冲突(例如,你可以使用window.$
作为另一个提议,比如Prototype.js)。
答案 3 :(得分:1)
自调用函数用于防止全局变量冲突。例如:
(function($){
//your code
})(jQuery);
在上面的例子中,如果它在使用jQuery和Prototype.js的页面中,你可以安全地引用$ object并且知道它是jquery的$ object而不是prototype的$ object。
答案 4 :(得分:1)
人们使用它是因为它是javascript中最强大的功能之一。阅读并欢迎使用Javascript。 http://javascript.crockford.com/private.html
答案 5 :(得分:0)
人们使用它的原因是模拟私有范围。匿名函数体中定义的任何变量都将从外部作用域隐藏(通常它是window
而垃圾window
与临时变量是不好的主意。)