12

对于模块模式,我正在做类似的事情:

(function(namespace) {
    // tons of code
    // blabla
})(window.myGlobalNamespace);

如何拆分代码?我可以想到几种方法,比如使用命名空间的层次结构,或者将对象扩展为window.myGlobalNamespace.additionalFunc = function () {//blabla}. 还有哪些方法?优缺点都有什么?哪一个被认为是更好的做法?

这两个答案都建议使用RequireJS。您能否解释一下 RequireJS 如何解决这些问题:

第一个.js:

(function(context) {
    var parentPrivate = 'parentPrivate';
})(window.myGlobalNamespace);

第二个.js:

(function(context) {
    this.childFunction = console.log('trying to access parent private field: ' + parentPriavte);
}(window.myGlobalNamespace.subNamspace);

主.js:

window.myGlobalNamespace.subNamspace.childFunction(); // doesn't work

人们可以做到

window.myGlobalNamespace.subNamspace.childFunction = function() {alert("a new function");}

改变我的代码的行为!

这里有两个问题:

  1. 我们不能有一个儿童可以访问但外部公众不能访问的字段(即受保护的)。有没有办法做到这一点?

  2. 如果不是,这意味着如果我们想要让 eparentPrivate 可访问,我们需要将其公开。然后用户将能够修改它!

更重要的是,所有公共功能都可以更改和替换。我不希望这种情况发生。

我不明白 RequireJS 是如何解决这些问题的。有人可以阐明一下吗?

4

4 回答 4

38

只有两种方法可以将 JavaScript 转换为 HTML:

  1. 内联-<script> some JavaScript </script>
  2. 链接——<script src='main.js'></script>

我知道这很明显,但我们需要为接下来的事情找到共同点。;)

JavaScript 不具备将其他 JavaScript 文件“导入”到它自己的能力。所有的“导入”都是在 HTML 中完成的。您可以通过以下几种方式执行此操作:

  • 将每个单独链接到 HMTL
  • 通过一些 JavaScript动态链接它们

    var script = document.createElement("script");
    script.src = "all.js";
    document.documentElement.firstChild.appendChild(script);
    
  • RequireJS这样的。RequireJS 使用异步模块定义 (AMD) API。它是用于定义模块的 JavaScript 机制,以便可以异步加载模块及其依赖项。

考虑将 JavaScript 分成单独文件的原因很重要。

  • 可维护性- 一次处理一件变得更容易
  • 可读性- 如果所有内容都在一个大文件中,则很难看出什么是什么
  • 分工- 让多个开发人员处理多个文件而不是一个大文件更容易
  • 重用——你的所有功能都可以分解成高度内聚的模块

单独的 JavaScript 文件不会使事物私有化,闭包使事物私有化。

现在,考虑一下,当一切都准备好投入生产时,您可以做的最好的事情是优化您的 JavaScript,将它全部合并到一个文件中,以便用户只有一个文件可以下载。


在 JavaScript 中处理私有变量时,您有时会想要访问它们。

  • 公共功能 - 可以更改
  • 特权函数 -可以访问私有变量的公共函数。
  • 但是,如果函数在Instance中,则只能在每个 Object 中更改

让我用一些代码来说明。

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

如果你想使用 RequireJS 来完成上面的事情,你可以。RequireJS 使用你和我已经在使用的模块模式。如果你想分离出文件,那么有两种方法可以做到这一点。

  1. 正常- 只需将您的 JS 文件设置为使用 RequireJS 并放入上述模块,只需稍作修改。
  2. Leveraged - 使用 RequireJS 的模块特性作为模块来设置闭包。这看起来可能更难弄清楚,但从长远来看它可能更有效。

注意:我还没有机会比较这两个选项,但为了完整起见,我想将它们包括在内。


您可能会发现以下参考资料很有帮助:

于 2013-12-03T18:52:20.163 回答
3

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.

http://requirejs.org

A good read: http://net.tutsplus.com/tutorials/javascript-ajax/principles-of-maintainable-javascript/

于 2013-12-01T11:32:04.617 回答
3

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

子类.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' 

注意:正如 Charles W. 所提到的,这种模式只适用protectedState于对象。如果它是一个字符串/整数,它将按值传递,并且在子类中所做的更改不会从父副本中可见。

于 2013-12-09T08:25:24.853 回答
1

模块化(拆分代码)与数据保护(隐藏数据)不同。

RequireJS 解决了模块化问题,而不是数据保护问题。或者换一种说法......无论尝试保护数据存在什么问题,以及存在任何保护数据的解决方案,无论有没有 RequireJS ,这些问题和解决方案都是相同的

RequireJS 实现了所有机制来指定模块之间的依赖关系,在需要时加载这些依赖关系,避免重新加载已经加载的东西,避免加载根本不需要的东西,快速更改模块的位置,有冗余等.

部署后,如果发现 RequireJS 太重,可以使用almond库。

我们不能有一个儿童可以访问但外部公众不能访问的字段(即受保护的)。有没有办法做到这一点?

如果您想要模块化(即您希望子项与父项分开编码),我认为这在 JavaScript 中是不可能的。可以让 child 和 parent 在同一个闭包中操作,但这不会是模块化的。无论有没有 RequireJS 都是如此。

如果不是,这意味着如果我们想要让 eparentPrivate 可访问,我们需要将其公开。然后用户将能够修改它!

如果要防止分配给parentPrivate,可以Object.freeze()在包含 的命名空间上使用parentPrivate

但是,我不知道各种浏览器对它的支持程度如何。如果 inparentPrivate本身是一个对象而不是原始值,那么如果您不希望它被代码的客户端修改,它也需要被冻结。并且一旦一个对象被冻结,它就会被所有人冻结,因此拥有该对象的模块不会得到特殊处理来修改它。冻结并没有隐藏任何东西。

或者你可以像在这个 RequireJS 示例中那样使用 setter 和 getter:

define(function () {
    'use strict';

    var writable = "initial value";

    var namespace = {
        get unwritable() { return writable; },
        doSomething: function () { writable = "changed value"; }

    };

    return namespace;

});

如果模块导入为parent,则parent.unwritable无法写入,但模块本身仍然可以更改写入返回的值writable。请注意,如果 getter 返回的返回值是对象而不是原始值,则调用者可以修改此对象

同样,无论您是否使用 RequireJS,这都是正确的。解决方案相同,问题相同,等等。

于 2013-12-04T14:03:40.130 回答