JavaScript 模块化中,立即执行函数表达式(IIFE)和非 IIFE 的区别

7

最近我在学习JavaScript中的IIFE和模块时,想到一个问题:如果立即调用函数表达式(IIFE)并不使其成为模块,那么它是如何创建模块的呢?

有人能分享一下以下代码的区别吗:

var MODULE = (function () {
var my = {},
    privateVariable = 1;

function privateMethod() {
    // ...
}

my.moduleProperty = 1;
my.moduleMethod = function () {
    // ...
};

return my;
}());

并且这段代码中该函数没有立即调用...

var MODULE = function () {
var my = {},
    privateVariable = 1;

function privateMethod() {
    // ...
}

my.moduleProperty = 1;
my.moduleMethod = function () {
    // ...
};

return my;
};

第二个代码块的意思是,模块只是一个返回对象的函数。如果我这样使用第二个变量:
var ModuleObj = Module();

这个代码块是否与我之前分享的第一个IIFE代码块相同?有点困惑...

2个回答

9

是的,你基本上已经明白了两者之间的区别,现在让我们看看为什么你可能更喜欢其中之一。

IIFE对于隔离作用域非常有用。它让你在IIFE内部保持所定义的变量私有,而不会污染其周围的全局空间。这是一个不错的方法,可以组合一个具有一些不需要潜伏的变量的函数。让我们稍微缩小这个例子。

var Counter = (function () {
  var count = 0;

  var counter = {
    add: function () {
      count++;
    },
    subtract: function () {
      count--;
    },
    getCount: function () {
      return count;
    }
  }
  return counter;
})();

Counter.add();
Counter.add();
Counter.getCount(); // 2
Counter.subtract();
Counter.getCount(); // 1

上面发生的事情是,我们能够在不泄露私有信息(例如count)的情况下组合这个“计数器”功能。如果其他东西可以意外地覆盖它,那就糟糕了。另外,由于我们可以立即将Counter分配给IFFE的结果——counter函数集,因此Counter现在等于那个函数集,并且counter可以保留对count的访问权限,因为它是在同一作用域中定义的。
其中的好处是我们能够将变量赋值给这个功能的组合。 IIFE基本上允许我们立即返回它内部的return内容。因为我们将Counter分配给IIFE,而IIFE返回其中的功能,所以Counter现在是一个完全功能的组件。
我们并不总是需要使用IIFE, 当您想要“隐藏”实现细节并返回API时,它真的很方便。
那么,如果我们有相同的东西,但不是IIFE——只是一个函数呢?
就像你的示例一样,我们必须调用它才能获得“实例”。
var CounterFactory = function () {
  var count = 0;
  var counter = {
    add: //...
    subtract: //...
    getCount: //...
  };
  return counter;
};

var CounterA = CounterFactory();
var CounterB = CounterFactory();

CounterA.add();
CounterA.add();
CounterA.getCount(); // 2

CounterB.add();
CounterB.getCount(); // 1

看出区别了吗?关键在于函数返回的内容。在第一个例子中,我们只得到单个Counter实例,这可能完全可以。而在第二个例子中,它更像是一个“工厂”——它生成counter的一个实例,我们可以多次调用它并获得多个实例。


1

好的,一个立即执行函数会运行其中的函数并将变量MODULE定义为该函数的返回值。另一个声明了MODULE变量为函数本身。

可以这样想(也可以在控制台中尝试以查看结果)。

此代码不运行console.log方法。

(function(){
    console.log('ran')
});

这段代码的作用是

(function(){
    console.log('ran')
})();

所以IIFE的整个目的是在执行任何操作之前运行函数,结尾处的();实现了这一点。
如果我们将未运行的代码赋值给一个值会发生什么?
var foo = (function(){
    console.log('ran')
});
foo();

我们有一个名为foo的函数可以执行。
那么如果我们只是分配并稍后运行它,立即调用函数表达式(IIFE)的意义在哪里?答案是局部变量,您可以稍后用于闭包。
console.log(num); //get undefined
(function(){
    var num = 'ran';
    console.log(num) //get 'ran'
})();
console.log(num); //get undefined

我们在函数中声明的值会留在函数中,其他任何东西都无法访问它们。这就是JavaScript运行的词法作用域。
只是为了好玩,让我们用它来做一个闭包。
var add = (function(){
    var num = 0;
    return function(){
        console.log(num++);
    }
})();
console.log(num) //get undefined
add() //get 1
add() //get 2
console.log(num) //still undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接