模块模式的要点?

Point of module pattern?

模块的主要好处(我听说过)是它们隐藏了私有变量。

var Module = (function() {
     var privateProperty = 'foo';

     return {
         publicProperty: '...',
         publicMethod: function(args) { ... }
     }
})();

但是 IIFE 不是执行此操作所必需的。如果你只是删除它,privateProperty 无论如何都会被隐藏。那么为什么要使用 IIFE?我正在尝试理解其中的原理。

编辑:

我一直在阅读没有 IIFE 的 privateProperty 将成为全局范围的一部分。我认为这是错误的。

如果我执行以下操作:

console.log(privateProperty); // => Reference Error: privateProperty isn't defined

我收到参考错误。如果我这样做:

console.log(Module.privateProperty) // => undefined

我不确定。如果我这样做:

var mod = new Module();
console.log(mod.privateProperty); // => undefined

我不确定。全局作用域无法访问 JS 中的局部作用域。

编辑 2:

test.js

var test = (function() {
  var privateProperty = 'foo';

  return {
    publicProperty: 'bar',
  }
})();

test.js

var test1 = function() {
  var privateProperty = 'foo';

  return {
    publicProperty: 'bar',
  }
};

index.html ...

<script src="./test.js"></script>
<script src="./test1.js"></script>

<script>
  console.log(test.privateProperty); // => undefined
  console.log(test1.privateProperty); // => undefined
</script>

当我尝试上述操作时,在这两种情况下我都无法访问 privateProperty。人们谈论的名称冲突在哪里? IIFE解决了什么?

没有模块,你必须使用另一个变量。

var Test = new test(someX);

然后以 Test.replace.

的形式访问属性

有了模块模式,你可以简单地使用

Module.publicProperty

加载后立即。

privateProperty would be hidden anyway

不,如果没有 IIFE,privateProperty 将是全局范围的 属性。

除非你谈论的是模块加载器,它(在幕后)基本上与 IIFE 做的一样,它将整个文件包装在一个函数中,有点像:

var factory = Function("require, module, exports, global", yourFileBody );

然后用正确的值调用工厂;这也是你拥有这些机制的原因;因为它们是作为函数参数注入的。

这就是这些 "private" 属性不会污染全局命名空间的原因。

编辑:

I tried an example without any module.exports, but I still don't understand what problem point IIFEs are solving. I posted the example in Edit 2

test1 是工厂,不是模块。让我们移除工厂并提取生成的模块,然后做一些小的改动,让这个私有状态有意义。 (将 public属性 转换为函数 "speak" 并在那里实际使用私有 property/value/state)

//the generted Module boils down to:
var test1 = { 
    name: "test1",
    speak() { 
        return this.name + " says " + private;
    } 
};
//and the private state also has to be kept somewhere:
var private = "Hello World";

现在让我们检查模块

console.log("test1", test1);
console.log("test1.speak()", test1.speak());
console.log("test1.private", test1.private);

很好,一切如预期
但是等等,这是什么?

console.log(
    "Look here:",
    private,
    this.private,
    window.private
)

哦不,有人曝光了我的隐私属性!每个人都可以看到它。
如果其他一些脚本也定义了私有 属性?

会发生什么
var private = "Bye Folks";
var test1 = { 
    name: "test2",
    speak() { 
        return this.name + " says " + private;
    } 
};

console.log("test2", test2);
console.log("test2.speak():", test2.speak());

很好,很好。怎么样...

console.log("test1.speak():", test1.speak());

哦不,test1坏了。它应该说 "Hello World" ...我希望有一种方法可以让我的 private 属性 真正私密,这样其他人就不会乱用它。

https://jsfiddle.net/crkguf6b/

@jro,你现在明白了吗?这样的工厂封装了私有状态,这样它就不会污染全局命名空间,也不会被一些不同的代码弄得一团糟;同时只暴露一个 public API。 您开始提问的 IIFE 实际上是一个匿名工厂,它会立即被调用以创建此 Object/Module 的一个实例,然后获取 GC。如顶部所示,模块加载器以相同的方式执行此操作。他们根据您的 JS 文件在幕后(或在预处理步骤中)创建这些工厂,并在必要时调用它们。

结论:

这不是语言固有的,private属性 是私有的。是 "artificial"。如果没有将 JS 文件包装在函数中的模块加载器,没有工厂,也没有 IIFE,private属性 就不是私有的。

也许这会随着 ES6 模块而改变。也许它会得到 JS 的固有部分,每个文件都被视为一个单独的模块,因此文件中的普通 var foo; 不会在全局命名空间中结束, 但在现在不是了。

您的 test1 文件包含普通函数,不是模块模式,也不是无模块模式。试试这个:

<script>
var testA = (function() {
  var privateVariable = 'bar A';

  return {
    publicProperty: 'foo' + privateVariable
  }
})();
</script>
<script>
var privateVariable = 'bar B';
var testB = {
  publicProperty: 'foo' + privateVariable
};
</script>
<script>
console.log(testA.publicProperty); // => 'foobar A'
console.log(testB.publicProperty); // => 'foobar B'
console.log(privateVariable); // => 'bar B'
</script>

这与任何东西的属性无关,它与脚本中的全局范围有关。