3

我见过专家使用下面来声明一个函数:

(function () {
  function f(n) {
        // Format integers to have at least two digits.
        return n < 10 ? '0' + n : n;
    }
  //etc

}());

例如 https://github.com/douglascrockford/JSON-js/blob/master/json.js

有人可以帮助我了解我们何时应该使用上述模式以及我们如何使用它?

谢谢。

4

3 回答 3

2

好吧,由于 ECMA6 还没有到来,函数是在 JS 中创建作用域的最佳方式。如果您在 IIFE(立即调用函数表达式)中包装一个变量声明,则不会全局创建该变量。函数声明也是如此。
如果你被赋予了清除所有全局变量脚本的看似艰巨的任务,你需要做的就是将整个脚本包装在一个简单的(function(){/*script here*/}());中,并且不会创建全局变量,以免它们是隐含的全局变量,但这只是一个懒惰的修复. 这种模式要强大得多。

我已经在这里这里这里更详细地解释了 IIFE 的使用。

基本的 JS 函数调用 live-cycle 的工作方式如下:

f();//call function
 ||
 ====> inside function, some vars are created, along with the arguments object
        These reside in an internal scope object
     ==> function returns, scope object (all vars and args) are GC'ed

像 JS 中的所有对象一样,一旦不再引用该对象,该对象就会被标记为 GC(垃圾收集)。但请考虑以下几点:

var foo = (function()
{
    var localFoo = {bar:undefined};
    return function(get, set)
    {
        if (set === undefined)
        {
            return localFoo[get];
        }
        return (localFoo[get] = set);
    }
}());

当 IIFE 返回时, foo 被分配了它的返回值,这是另一个函数。NowlocalFoo被声明在 IIFE 的范围内,没有办法直接访问该对象。乍一看,您可能期望localFoo被 GC 处理。
但是请稍等,正在返回的函数(并分配给foo仍然引用该对象,因此它不能被 gc'ed。换句话说:范围对象比函数调用的寿命更长,并且创建了一个闭包。

然后,在变量超出范围或重新分配另一个值并且对返回函数的所有引用都丢失之前,该localFoo对象不会被 GC'ed 。foo

看看其中一个链接的答案(带有图表的答案),在那个答案中有一个文章的链接,我从那里偷了我使用的图像。如果还没有,那应该为您解决问题。

IIFE 可以不返回任何内容,但无论如何都会公开其范围:

var foo = {};
(function(obj)
{
    //obj references foo here
    var localFoo = {};
    obj.property = 'I am set in a different scope';
    obj.getLocal = function()
    {
        return localFoo;
    };
}(foo));

此 IIFE 不返回任何内容(隐含undefined),但console.log(foo.getLocal())将记录空对象文字。foo本身也会被赋值property。但是等等,我可以为你做一个更好的。假设 foo 已经通过上面的代码一次:

var bar = foo.getLocal();
bar.newProperty = 'I was added using the bar reference';
bar.getLocal = function()
{
    return this;
};
console.log(foo.getLocal().newProperty === bar.newProperty);
console.log(bar ==== foo.getLocal());
console.log(bar.getLocal() === foo.getLocal().getLocal());
//and so on

这会记录什么?事实上,它会true一次又一次地记录。对象在 JS 中永远不会被复制,它们的引用被复制,但对象始终是相同的。在某个范围内更改一次,这些更改将在所有引用中共享(逻辑上)。
这只是为了向您展示闭包一开始可能很难理解,但这也显示了它们的强大功能:您可以通过各种 IIFE 传递一个对象,每次设置一个可以访问它自己的新方法,其他方法无法达到的独特范围。

注意
关闭器对于 JS 引擎来说并不是那么容易进行垃圾收集,但最近,这不再是一个大问题了
也花点时间谷歌这些条款:

IIFE 也可以命名为函数,但唯一可以引用该函数的地方是该函数的范围内:

(function init (obj)
{
    //obj references foo here
    var localFoo = {};
    obj.property = 'I am set in a different scope';
    obj.getLocal = function()
    {
        return localFoo;
    };
    if (!this.wrap)
    {//only assign wrap if wrap/init wasn't called from a wrapped object (IE foo)
        obj.wrap = init;
    }
}(foo));
var fooLocal = foo.getLocal();
//assign all but factory methods to fooLocal:
foo.wrap(fooLocal);
console.log(fooLocal.getLocal());//circular reference, though
console.log(init);//undefined, the function name is not global, because it's an expression

这只是一个基本示例,说明如何使用闭包创建包装器对象......

于 2013-06-05T12:10:59.007 回答
1

Browser-based JavaScript only has two scopes available: Global and Function. This means that any variables you create are in the global scope or confined to the scope of the function that you are currently in.

Sometimes, often during initialization, you need a bunch of variables that you only need once. Putting them in the global scope isn't appropriate bit you don't want a special function to do it.

Enter, the immediate function. This is a function that is defined and then immediately called. That's what you are seeing in Crockford's (and others') code. It can be anonymous or named, without defeating the purpose of avoiding polluting the global scope because the name of the function will be local to the function body.

It provides a scope for containing your variables without leaving a function lying around. Keeps things clean.

于 2013-06-05T12:14:36.923 回答
1

那么上面的模式被称为立即函数。这个函数做了三件事:-

此代码的结果是一个表达式,它在单个语句中执行以下所有操作:

  1. 创建一个函数实例
  2. 执行函数
  3. 丢弃该函数(因为在语句结束后不再有对它的任何引用)

JS 开发人员使用它来创建变量和函数,而不会污染全局空间,因为它为变量和函数创建了自己的私有范围。

在上面的示例中,函数 f(){} 位于立即函数的私有范围内,您不能在全局或窗口范围内调用此函数。

于 2013-06-05T12:11:43.240 回答