我正在为Javascript创建一个游戏引擎,或者更像是一个有用的类和函数的大型库。我计划将它用于服务器端和客户端的一些科学模拟,因此功能范围将非常广泛,但它总是围绕虚拟世界(例如游戏)。
但是,我不知道如何把它包起来。如果我只提供所有类,它将污染全局命名空间,这是非常糟糕的。我可以将所有内容放在一个对象中,作为命名空间吗?框架本身应该是一个可以实例化的类吗?如果选择了后一个选项,我该如何处理类(构造函数)中的类?
var Engine = function()
{
this.someVar = 4;
}
Engine.prototype.Scene = function()
{
this.entities = [];
//What if the scene object needs some classes that are in the engine? How does it get it's parent engine object?
}
Engine.prototype.Scene.prototype.render = function()
{
//"this" should now represent an instance of a scene. But how can I get someVar from the engine? How can I traverse up in the hierarchy of classes?
}
答案 0 :(得分:2)
我更喜欢使用有时被称为“revealing module”(...模式)的东西。它看起来像:
var Engine = (function($)
{
$ = $ || {};
var someVar = 4;
$.Scene = function()
{
this.entities = [];
}
$.Scene.prototype.render = function()
{
// this function can access someVar just fine because of JavaScript's scoping rules
}
return $;
})(Engine);
这使用所谓的立即调用的函数表达式(以下称为 IIFE )在Engine对象中形成闭包。由于JavaScript's handling of scope,{II}中定义的任何函数都可以访问someVar
。然而,其含义是,如果想要引用您在IIFE中定义的someVar
,任何函数都无法定义它自己的someVar
。
魔术来自return语句。您可以看到返回了一个对象,在此对象中,您必须定义任何您想要“公开”的对象。
然后可以通过Engine.Scene
访问构造函数,实用程序方法等,这可以很好地命名代码。
对于$
参数,这是为了你可以将Engine
传递给每个文件中的函数,添加一些方法/属性/构造函数(如果不是,则创建一个新的方法/属性/构造函数)然后将返回值传递给另一个IIFE以进一步扩展。
这是许多常用JavaScript框架中使用的方法,包括jQuery,dojo和LimeJS
scene.js:
var Engine = (function ($) {
// this creates a new object if Engine is undefined in the
// invocation below, and keeps the old object otherwise.
// alternatively: if ($ === undefined) { $ = new Object; }
$ = $ || {};
$.foo = "foo";
$.Scene = function () {
// blah blah
}
// Engine holds either a newly created object,
// or the old one if it was already defined
return $;
})(Engine);
sprite.js:
var Engine = (function ($) {
$ = $ || {};
$.Sprite = function () {
// totally works
this.bar = $.foo;
}
return $;
})(Engine);
然后您可以使用以下内容:
<script type="text/javascript" src="bar.js"></script>
<script type="text/javascript" src="foo.js"></script>
<script type="text/javascript">
var mySprite = new Engine.Sprite;
var myScene = new Engine.Scene;
</script>
您可以将$
替换为您喜欢的任何内容,$$
是常见的,或者您可以聪明。它只是您要添加的全局对象的占位符。
答案 1 :(得分:0)
我认为你不需要甚至不应该像这样组织你的课程。尽管Scene和Engine类是相关的,但Scene类不必是Render的子代。使用平面类层次结构,这将更容易维护和缩放。
最后,您确实应该将所有这些类放在同一名称空间中,以免污染全局名称空间。
// Define the namespace only if it doesn't already exist. That
// way you can split the definition of your classes in various
// files, without having to worry in which order they are loaded.
if (typeof gameEngine === 'undefined') gameEngine = {};
gameEngine.Engine = function()
{
this.someVar = 4;
}
gameEngine.Scene = function()
{
this.entities = [];
}
// Add a new function to define which
// scene the Engine should render:
gameEngine.Engine.prototype.setScene = function(scene)
{
this.scene = scene;
}
gameEngine.Engine.prototype.render = function()
{
// render this.scene
}