1

一段时间以来我一直在用 JS 编程,但我从来没有遇到过使用即时函数的需要,例如:

(function(){
    console.log('hello, I am an immediate function');
}())

如果我只写:

console.log('hello, I am an immediate function');

? 无论如何,我无权访问此功能(它没有分配到任何地方)。我认为(但我不确定)我可以在没有即时功能的情况下实现一切——那么人们为什么要使用它呢?

4

6 回答 6

5

更新:
我发现了这个问题,我确实在其中详细介绍了闭包,并用很少的图纸来阐明如何使用闭包和函数范围(以及它们如何为您提供帮助)。

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

于 2013-06-20T12:06:40.133 回答
4

立即调用的函数表达式用于创建命名空间。我不会准确解释为什么 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 臭名昭著的循环问题?

于 2013-06-20T12:06:38.267 回答
1

开发人员使用即时函数来声明私有作用域。例如...

(function($){
   $("div").html("example");
})(jQuery);

使用此代码,您不需要$全局范围内的变量,就window.jQuery在外部。当您想使用另一个库在window对象中使用的变量名时,它可以防止可能的冲突(例如,您可以将其window.$用于另一个提议,如 Prototype.js)。

于 2013-06-20T11:35:42.060 回答
1

自调用函数用于防止全局变量冲突。例如:

(function($){
    //your code 
})(jQuery);

在上面的例子中,如果它是在一个使用 jQuery 和 Prototype.js 的页面中,你可以安全地引用 $object 并且知道它是 jquery 的 $ 对象而不是原型的 $ 对象。

于 2013-06-20T11:37:53.813 回答
1

人们使用它是因为它是 javascript 中最强大的功能之一。阅读并欢迎使用 Javascript。http://javascript.crockford.com/private.html

于 2013-06-20T11:40:33.003 回答
0

人们使用它的原因是模拟私有作用域。在匿名函数体中定义的任何变量都将从外部范围隐藏(通常它是带有临时变量的window垃圾是坏主意)。window

于 2013-06-20T11:36:55.620 回答