javascript 模块模式 - 如何将一个模块的代码拆分为不同的 js 文件?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20311604/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Module pattern- How to split the code for one module into different js files?
提问by Boyang
For the module pattern, I'm doing something like:
对于模块模式,我正在做类似的事情:
(function(namespace) {
// tons of code
// blabla
})(window.myGlobalNamespace);
How do I split the code? I can think of a few ways, like use a hierachy of namespaces, or expand the object outside by window.myGlobalNamespace.additionalFunc = function () {//blabla}
. What are the other ways? What are the pros and cons? Which one is considered better practice?
我如何拆分代码?我可以想到几种方法,例如使用命名空间的层次结构,或将对象扩展到window.myGlobalNamespace.additionalFunc = function () {//blabla}
. 其他方式是什么?优缺点都有什么?哪一种被认为是更好的做法?
Both of the two answers suggest RequireJS. Can you please explain how RequireJS can solve these problems:
这两个答案都建议 RequireJS。能否请您解释一下 RequireJS 如何解决这些问题:
first.js:
第一个.js:
(function(context) {
var parentPrivate = 'parentPrivate';
})(window.myGlobalNamespace);
second.js:
第二个.js:
(function(context) {
this.childFunction = console.log('trying to access parent private field: ' + parentPriavte);
}(window.myGlobalNamespace.subNamspace);
main.js:
主要.js:
window.myGlobalNamespace.subNamspace.childFunction(); // doesn't work
And people can do
人们可以做到
window.myGlobalNamespace.subNamspace.childFunction = function() {alert("a new function");}
to change my code's behaviour!
改变我的代码的行为!
Here, there are two problems:
这里,有两个问题:
We can't have a field that's accessible by child but not to outside public (i.e. protected). Is there any way to achieve that?
If not, meaning if we wanteparentPrivate to be accessible, we need to make it public. Then the user will be able to modify it!
我们不能有一个孩子可以访问但外部公众(即受保护)不能访问的字段。有什么方法可以实现吗?
如果没有,这意味着如果我们想要访问 parentPrivate,我们需要将其公开。然后用户就可以修改它了!
What's more, all the public functions can be altered and replaced. I don't want that to happen.
更重要的是,所有公共功能都可以更改和替换。我不希望这种情况发生。
I don't see how RequireJS solves these problems. Can someone shed some light?
我不明白 RequireJS 如何解决这些问题。有人可以解释一下吗?
回答by Joshua Wilson
There are only 2 ways to get JavaScript into HTML:
将 JavaScript 转为 HTML 的方法只有两种:
- Inline-
<script> some JavaScript </script>
- Link-
<script src='main.js'></script>
- 内联-
<script> some JavaScript </script>
- 链接——
<script src='main.js'></script>
I know this is obvious but we need that common ground for what comes next. ;)
我知道这很明显,但我们需要为接下来的事情达成共识。;)
JavaScript does not have the ability to "import" other JavaScript files into it's self. All the "importing" is done in the HTML. You can do this several ways:
JavaScript 没有能力将其他 JavaScript 文件“导入”到它自己的文件中。所有的“导入”都是在 HTML 中完成的。您可以通过以下几种方式执行此操作:
- Linkeach one individually into the HMTL
Dynamically linkthem in through some JavaScript
var script = document.createElement("script"); script.src = "all.js"; document.documentElement.firstChild.appendChild(script);
Librarylike RequireJS. RequireJS uses Asynchronous Module Definition (AMD) API. It is the JavaScript mechanism for defining modules such that the module and its dependencies can be asynchronously loaded.
- 将每个单独链接到 HMTL
通过一些 JavaScript动态链接它们
var script = document.createElement("script"); script.src = "all.js"; document.documentElement.firstChild.appendChild(script);
类似RequireJS 的库。RequireJS 使用异步模块定义 (AMD) API。它是用于定义模块的 JavaScript 机制,以便可以异步加载模块及其依赖项。
It is import to consider reasons for separating JavaScript into separate files.
考虑将 JavaScript 分成单独文件的原因很重要。
- Maintainability- it becomes easier to work on one piece at a time
- Readability- if everything is in one big file it is very hard to see what is what
- Division of Labor- it is easier to have multiple developers working on multiple files instead of one big one
- Reuse- all your functions can be broken up into highly cohesivemodules
- 可维护性- 一次处理一件变得更容易
- 可读性- 如果所有内容都在一个大文件中,则很难看出什么是什么
- 分工- 让多个开发人员处理多个文件而不是一个大文件更容易
- 重用- 您的所有功能都可以分解为高度内聚的模块
Separate JavaScript files DO NOTmake things Private, Closures make things Private.
单独的 JavaScript 文件不会使事情成为私有,闭包使事情成为私有。
Now, consider at the end of the day when everything is ready for production the best thing you could do is Optimizeyour JavaScript by combining it all into one file so that the user only has one file to download.
现在,考虑在一天结束时,当一切都准备好投入生产时,您可以做的最好的事情是通过将所有内容合并到一个文件中来优化您的 JavaScript,以便用户只能下载一个文件。
When dealing with Private variables in JavaScript, you will at some point want to access them.
在 JavaScript 中处理私有变量时,您有时会想要访问它们。
- Publicfunction - can be altered.
- Privilegedfunction - a Publicfunction that can access the Privatevariable.
- However if the function is in an Instancethen it can only be altered in each Object.
- 公共功能 - 可以更改。
- 特权函数 -可以访问Private变量的公共函数。
- 但是,如果函数在Instance 中,则只能在每个 Object 中更改它。
Let me illustrate with some code.
让我用一些代码来说明。
module-test.html and main.js (merged first.js, second.js, and main.js for easier testing)
module-test.html 和 main.js(合并 first.js、second.js 和 main.js 以便于测试)
var MODULE = (function () {
//Private variables
var privateParent,
app;
privateParent = 'parentPrivate';
return app = {
//Privileged method
getPrivateParent: function() {
return privateParent;
}
};
}());
MODULE.sub = (function (parentApp) {
//Private variables
var childMessage,
Constr;
childMessage = ' - trying to access parent private field: ' + parentApp.getPrivateParent(); //prints parentPrivate
Constr = function () {
this.childF = this.childFunction();
};
//Constructor
Constr.prototype = {
constructor: MODULE.sub,
version: "1.0",
childFunction: function () {
$("#testing-div").append(childMessage + "</br>");
}
};
return Constr;
}(MODULE));
//We could just as easily print to the console, but the 'append' allows us to display the results on the page.
$("#testing-div").append("This first part shows what <b>does not work</b>; everything is 'undefined'. " + "</br>");
$("#testing-div").append("You are unable to access the var or func directly. " + "</br>");
$("#testing-div").append("MODULE.privateParent = " + MODULE.privateParent + "</br>");
$("#testing-div").append("MODULE.app = " + MODULE.app + "</br>");
$("#testing-div").append("MODULE.sub.childMessage = " + MODULE.sub.childMessage + "</br>");
$("#testing-div").append("MODULE.sub.Constr = " + MODULE.sub.Constr + "</br>");
$("#testing-div").append("MODULE.sub.childFunction = " + MODULE.sub.childFunction + "</br>");
$("#testing-div").append("END lesson. You must access childFunction() through the <b>new</b> operator." + "</br>");
$("#testing-div").append("----------------------------------------------------" + "</br>");
$("#testing-div").append("Let's see if making an instance of the Object works" + "</br>");
var test = new MODULE.sub();
test.childFunction(); //run the method
$("#testing-div").append("Looks like it did!!!!" + "</br>");
$("#testing-div").append("----------------------------------------------------" + "</br>");
$("#testing-div").append("Now let's try to change the childFunction() ?" + "</br>");
test.childFunction = function() {$("#testing-div").append(" - This is a new function." + "</br>");}
test.childFunction(); // altered version
$("#testing-div").append("Looks like it was changed. :(" + "</br>");
$("#testing-div").append("----------------------------------------------------" + "</br>");
$("#testing-div").append("Does it stay changed?" + "</br>");
var test2 = new MODULE.sub();
test2.childFunction(); // doesn't work
$("#testing-div").append("NO, it was only Overriden in the 'test' Object. It did not effect all the other new objects. :)" + "</br>");
$("#testing-div").append("----------------------------------------------------" + "</br>");
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Module Test</title>
<!-- <script data-main="scripts/main" src="scripts/require.js"></script> -->
</head>
<body>
<h1>This is a test for separate Modules and Private variables.</h1>
<div id="testing-div">
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="main.js"></script>
</body>
</html>
If you want to use RequireJS to accomplish the above, you can. RequireJS uses the Module Pattern which is what you and I are already using. If you want to separate out the files then there are two ways to do this.
如果你想使用RequireJS来完成上面的,你可以。RequireJS 使用你我已经在使用的模块模式。如果你想分离出文件,那么有两种方法可以做到这一点。
- Normal- Just set up your JS files to use RequireJS and drop in the above Modules with only a slight modification.
- Leveraged- Use the Module nature of RequireJS as the modules to set up the closures. This looks like it may be harder to figure out but it may be more efficient in the long run.
- 普通- 只需将您的 JS 文件设置为使用 RequireJS,只需稍加修改即可放入上述模块。
- 杠杆- 使用 RequireJS 的模块特性作为模块来设置闭包。这看起来可能更难弄清楚,但从长远来看可能更有效。
NOTE: I haven't had a chance to compare these two options yet but wanted to include them for completeness.
注意:我还没有机会比较这两个选项,但为了完整起见,我想将它们包括在内。
You may find the following references helpful:
您可能会发现以下参考资料很有帮助:
回答by leaksterrr
It sounds like what you're after is Require JS. I use this in almost all of my builds now. Alternatively you can have a look at the revealing module pattern as a fallback but for what you're after it sounds like Require is much more suitable.
听起来您想要的是 Require JS。我现在几乎在所有构建中都使用它。或者,您可以将揭示模块模式视为后备,但对于您所追求的,听起来 Require 更合适。
A good read: http://net.tutsplus.com/tutorials/javascript-ajax/principles-of-maintainable-javascript/
一本好书:http: //net.tutsplus.com/tutorials/javascript-ajax/principles-of-maintainable-javascript/
回答by roo2
Protected variables in javascript can be achieved by passing in the protected variables as a dependency. The subclass must be created within the parent as only there does it have access to the protected variables. Example jsFiddle
javascript 中的受保护变量可以通过将受保护变量作为依赖项传入来实现。子类必须在父类中创建,因为只有它才能访问受保护的变量。示例 jsFiddle
App = window.App || {};
App.ParentClass = (function(){
var protectedState = {
protectedVar: 'wow such protection'
}
return{
SubClassInstance: App.SubClass(protectedState), //make the subclass accessible from outside
}
})(); //closure gives us privacy
SubClass.js
子类.js
App = window.App || {};
App.SubClass = function(protectedState){
return {
logProtectedVar : function(){
//can access protectedState of parent
console.log(protectedState.protectedVar);
}
}
}// I require protected state to work
main.js
主文件
// protected variable is accessible from parent and subclass and nowhere else
App.ParentClass.SubClassInstance.logProtectedVar();
// 'wow such protection'
NOTE: as Charles W. mentioned, this pattern only works when protectedState
is an object. If it were a string/int it would be passed by value and changes made in the subclass would not be visible from the parents copy.
注意:正如 Charles W. 所提到的,这种模式仅适用protectedState
于对象。如果它是一个字符串/整数,它将按值传递,并且子类中所做的更改从父副本中将不可见。
回答by Louis
Modularization (splitting the code) is not the same as data protection (hiding data).
模块化(拆分代码)与数据保护(隐藏数据)不同。
RequireJS solves the modularization issue, not the data-protection issue. Or to put it differently... Whatever issues exist with trying to protect data and whatever solutions exist to protect data, these issues and solutions are the same with or without RequireJS.
RequireJS 解决了模块化问题,而不是数据保护问题。或者换一种说法......无论尝试保护数据存在什么问题,以及存在什么保护数据的解决方案,这些问题和解决方案在有或没有 RequireJS 的情况下都是相同的。
RequireJS implements all the mechanics to specify dependencies between modules, to load these dependencies onlyas needed, to avoid reloading things that have already been loaded, avoid loading things that are not required at all, quickly change the location of modules, have redundancy, etc.
RequireJS 实现了所有机制来指定模块之间的依赖关系,只在需要时加载这些依赖项,避免重新加载已经加载的东西,避免加载根本不需要的东西,快速改变模块的位置,有冗余等.
After deployment if one finds RequireJS somehow too heavy, there's the almondlibrary that can be used instead.
部署后,如果发现 RequireJS 不知何故太重,则可以使用杏仁库。
We can't have a field that's accessible by child but not to outside public (i.e. protected). Is there any way to achieve that?
我们不能有一个孩子可以访问但外部公众(即受保护)不能访问的字段。有什么方法可以实现吗?
If you want modularization (i.e. you want the child to be coded separately from the parent), I do not believe this is possible in JavaScript. It would be possible to have child and parent operate in the same closurebut then this would not be modular. This is true with or without RequireJS.
如果您想要模块化(即您希望子代与父代分开编码),我不相信这在 JavaScript 中是可能的。让 child 和 parent 在同一个闭包中操作是可能的,但这将不是模块化的。无论是否使用 RequireJS,都是如此。
If not, meaning if we wanteparentPrivate to be accessible, we need to make it public. Then the user will be able to modify it!
如果没有,这意味着如果我们想要访问 parentPrivate,我们需要将其公开。然后用户就可以修改它了!
If you want to prevent assigning to parentPrivate
, you can use Object.freeze()
on the namespace that contains parentPrivate
.
如果要防止分配给parentPrivate
,可以Object.freeze()
在包含parentPrivate
.
However, I don't know how well it is supported by various browsers. And if what is in parentPrivate
is itself an object rather than a primitive value, it also needs to be frozen if you don't want it to be modified by clients of your code. And once an object is frozen, it is frozen for everyone so the module that ownsthe object does not get special treatment to modify it. And freezing does not hideanything.
但是,我不知道各种浏览器对它的支持情况如何。如果内容parentPrivate
本身是一个对象而不是原始值,如果您不希望它被代码的客户端修改,它也需要被冻结。并且一旦一个对象被冻结,它对每个人都是冻结的,因此拥有该对象的模块不会得到特殊处理来修改它。冻结并没有隐藏任何东西。
Or you could use setters and getters like in this RequireJS example:
或者你可以在这个 RequireJS 示例中使用 setter 和 getter:
define(function () {
'use strict';
var writable = "initial value";
var namespace = {
get unwritable() { return writable; },
doSomething: function () { writable = "changed value"; }
};
return namespace;
});
If the module is imported as parent
, then parent.unwritable
cannot be written to but the module itself can still change the value returned by writing to writable
. Note that if the return value returned by the getter is an object rather than a primitive value, this object canbe modified by the caller.
如果模块被导入为parent
,则parent.unwritable
无法写入,但模块本身仍然可以通过写入 来更改返回的值writable
。注意如果getter返回的返回值是一个对象而不是原始值,这个对象可以被调用者修改。
Again, this is true whether or not you use RequireJS. The solutions are the same, the problems are same, etc.
同样,无论您是否使用 RequireJS,这都是正确的。解决方案是一样的,问题是一样的,等等。