一段时间以来我一直在用 JS 编程,但我从来没有遇到过使用即时函数的需要,例如:
(function(){
console.log('hello, I am an immediate function');
}())
如果我只写:
console.log('hello, I am an immediate function');
? 无论如何,我无权访问此功能(它没有分配到任何地方)。我认为(但我不确定)我可以在没有即时功能的情况下实现一切——那么人们为什么要使用它呢?
一段时间以来我一直在用 JS 编程,但我从来没有遇到过使用即时函数的需要,例如:
(function(){
console.log('hello, I am an immediate function');
}())
如果我只写:
console.log('hello, I am an immediate function');
? 无论如何,我无权访问此功能(它没有分配到任何地方)。我认为(但我不确定)我可以在没有即时功能的情况下实现一切——那么人们为什么要使用它呢?
更新:
我发现了这个问题,我确实在其中详细介绍了闭包,并用很少的图纸来阐明如何使用闭包和函数范围(以及它们如何为您提供帮助)。
IIFE 作为函数对象,是在 JS中创建真实作用域的唯一方法。它们到处都在使用。你曾经使用过 jQuery 或其他一些库吗?那么你已经使用了使用 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. 原因是超时回调没有得到它自己的' 值副本,但它引用了变量。该变量可以根据需要更改其值,当调用超时回调时,它将记录最后分配的任何值。定义回调时未分配值。iiii
没有过多的细节,我提到了对象的私有属性。好吧,这里有一个例子让你弄清楚为什么 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
立即调用的函数表达式用于创建命名空间。我不会准确解释为什么 IIFE 如此重要——Ben Alman 在这方面做得非常好。尽管如此,我会说 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 臭名昭著的循环问题?
开发人员使用即时函数来声明私有作用域。例如...
(function($){
$("div").html("example");
})(jQuery);
使用此代码,您不需要$全局范围内的变量,就window.jQuery在外部。当您想使用另一个库在window对象中使用的变量名时,它可以防止可能的冲突(例如,您可以将其window.$用于另一个提议,如 Prototype.js)。
自调用函数用于防止全局变量冲突。例如:
(function($){
//your code
})(jQuery);
在上面的例子中,如果它是在一个使用 jQuery 和 Prototype.js 的页面中,你可以安全地引用 $object 并且知道它是 jquery 的 $ 对象而不是原型的 $ 对象。
人们使用它是因为它是 javascript 中最强大的功能之一。阅读并欢迎使用 Javascript。http://javascript.crockford.com/private.html
人们使用它的原因是模拟私有作用域。在匿名函数体中定义的任何变量都将从外部范围隐藏(通常它是带有临时变量的window垃圾是坏主意)。window