2016-08-27 55 views
-1

模块的主要优点(我听说过)是他们隐藏私有变量。模块模式的要点?

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

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

但是,IIFE没有必要这样做。如果你只是删除它,privateProperty将被隐藏。那么为什么要使用IIFE?我试图理解理由。

编辑:

我继续读那privateProperty没有IIFE将是全球范围的一部分。我认为这是错误的。

如果我做到以下几点:

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

我得到一个参考错误。如果我这样做:

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

我得到undefined。如果我这样做:

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

我得到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解决?

+1

“* privateProperty将被隐藏”*“ - 否?它与'Module'的范围相同,这正是我们在这里试图避免的吗? – Bergi

+0

你在说什么es6模块? – bitten

+0

@Bergi为什么我们试图避免模块中的范围。我认为私有变量的意义在于它们在模块之外(或者在函数范围之外)是隐藏的。 – jro

回答

1

privateProperty将反正

隐藏不,没有IIFE,privateProperty将是全球范围内的财产。

除非你是在谈论一个模块加载器,它(幕后)基本上不一样的IIFE,它包装了整个文件的功能,有点像:

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

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

这就是这些“私人”属性如何不污染全局名字空间。

编辑:

我试过一个例子没有任何module.exports,但我还是不明白IIFEs正在解决哪些问题点。我发布的例子编辑2

test1是一个工厂,而不是一个模块。让我们移除工厂并提取生成的模块,让我们稍作修改,以使这个私有状态有意义。 (转公物变成“说话”,并实际使用的私有财产/值/状态存在的函数)如预期
别急

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

结论:

这不是固有的语言,即privateProperty是私人的。这是“人造”。如果没有将你的JS文件封装在函数中的模块加载器,没有工厂,也没有IIFE,privateProperty就不是私有的。

也许这将随ES6模块而改变。也许它会得到JS的一个固有部分,即每个文件都被看作是一个单独的模块,因此该文件中的纯文本不会在全局命名空间中结束,但现在不是。

+0

如果我有上面的Module,除了没有IIFE,我不能从全局范围访问privateProperty。 – jro

+0

你怎么“加载”这个模块? – Thomas

+0

module.exports or export – jro

0

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

var Test = new test(someX); 

然后访问属性为Test.replace

与模块的模式,你可以简单地使用

Module.publicProperty 

加载后立即。

+0

我想我提出了一个分心的问题。所以我继续并删除它。问题是IIFE如何做隐藏私有变量的任何事情。 JS的范围工作似乎是多余的。 – jro

+0

它们都使用函数作用域以相同的方式隐藏变量。它们之间的区别在于它们使用方式的其他方面,我的回答描述了这些差异之一。 – Barmar

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>

这不是任何事物的属性,它是关于在脚本全局范围内。