点模块模式?

时间:2016-08-27 16:41:24

标签: javascript module iife

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

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解决?

3 个答案:

答案 0 :(得分:1)

  

privateProperty无论如何都会隐藏

不,如果没有IIFE,privateProperty将成为全球范围内的财产。

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

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

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

这些&#34;私人&#34;属性不会污染全局命名空间。

修改

  

我尝试了一个没有任何module.exports的例子,但我仍然不明白IIFE正在解决什么问题。我在Edit 2

中发布了这个例子

test1是工厂,而不是模块。让我们删除工厂并提取生成的模块,让我们做一些小改动,以便这个私有状态有意义。 (将publicProperty变成一个函数&#34;说&#34;并实际使用私有属性/值/状态)

//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坏了。它应该说&#34; Hello World&#34; ......我希望有办法让我的private财产真正私密,这样别人就不会搞砸了。

https://jsfiddle.net/crkguf6b/

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

<强>结论:

该语言不是固有的,私有属性是私有的。它&#34;人工&#34;。没有将JS文件包装在函数中的模块加载器,没有工厂,没有IIFE,privateProperty也不会私有。

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

答案 1 :(得分:1)

您的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>

它不是关于任何东西的属性,而是关于脚本中的全局范围。

答案 2 :(得分:0)

如果没有该模块,则必须使用另一个变量。

var Test = new test(someX);

然后以Test.replace访问属性。

使用模块模式,您只需使用

即可
Module.publicProperty
加载后立即