グローバル変数は、システム全体の依存関係を持つオブジェクト用に予約される必要があり、その名前は曖昧さを避け、名前の競合のリスクを最小限に抑える必要があります。実際には、これは、絶対に必要な場合を除き、グローバル オブジェクトの作成を避ける必要があることを意味します。
しかし、まあ、あなたはすでにこれを知っていました...
それで、あなたはそれについて何をしましたか?従来のアプローチでは、グローバルを排除するための最良の戦略は、基礎となるモジュールやサブシステムの実際の名前空間として機能する少数のグローバル オブジェクトを作成することであると教えてくれます。名前空間に対するいくつかのアプローチを検討し、最後に、James Edwards による最近の記事に基づいて私が考案した、エレガントで安全かつ柔軟なソリューションを紹介します。
私は、名前空間タグが実際にハードコーディングされているソリューションの包括的な用語として 静的名前空間
を使用します。はい、あるネームスペースを別のネームスペースに再割り当てすることはできますが、新しいネームスペースは古いネームスペースと同じオブジェクトを参照します。 静态命名空间
作为那些命名空间标签实际上硬编码的解决方案的涵盖性术语。是的,你可以将一个命名空间重新分配给另一个,不过新的命名空间将会引用和旧的那一个同样的对象。
最基础的方法。这样非常冗长,并且如果你还想重命名这些命名空间,你就有得活儿干了。不过它是安全和清楚明白的。
var myApp = {} myApp.id = 0; myApp.next = function() { return myApp.id++; } myApp.reset = function() { myApp.id = 0; } window.console && console.log( myApp.next(), myApp.next(), myApp.reset(), myApp.next() ); //0, 1, undefined, 0
你也可以通过使用this
引用兄弟属性来使将来的维护更轻松一些,不过这有一点冒险因为没有什么能阻止你的那些命名空间里的方法被重新分配。
var myApp = {} myApp.id = 0; myApp.next = function() { return this.id++; } myApp.reset = function() { this.id = 0; } myApp.next(); //0 myApp.next(); //1 var getNextId = myApp.next; getNextId(); //NaN whoops!
现在我们只需要引用命名空间名一次,因此之后改变名字更简单了一些(假设你还没反复引用这个命名空间)。仍有一个危险是this
的值可能会抛出一个『惊喜』 – 不过假设在一个对象字面结构里定义的对象不会被重新分配相对安全一点。
var myApp = { id: 0, next: function() { return this.id++; }, reset: function() { this.id = 0; } } window.console && console.log( myApp.next(), myApp.next(), myApp.reset(), myApp.next() ) //0, 1, undefined, 0
我发现自己最近用模块模式
更多。逻辑被一个方法包装从全局域隔离开了(通常是自调用的),它返回一个代表这个模块公开接口的对象。通过立即调用这个方法并分配结果给一个命名空间变量,我们就锁住了这个命名变量中模块的 API。此外,任何没有包括在返回值中的变量将永远保持私有,只对引用他们的公开方法可见。
var myApp = (function() { var id= 0; return { next: function() { return id++; }, reset: function() { id = 0; } }; })(); window.console && console.log( myApp.next(), myApp.next(), myApp.reset(), myApp.next() ) //0, 1, undefined, 0
如上对象字面量例子,命名空间名字可以轻易更换,不过还有额外优势:对象字面量是四班的 – 它全是关于属性分配,没有支持逻辑的空间。此外,所有属性必须被初始化,并且属性值无法轻易跨对象引用(因此,比如,内部闭包就不可能使用了)。模块模式没有任何上述约束,并且给我们额外的隐私福利。
我们也可以将这一节称为命名空间注入
。命名空间由一个直接引用方法包装内部
的代理代表 – 这意味着我们不再需要打包分配给命名空间的返回值。这让命名空间定义变得更灵活并且让拥有多个存在于独立命名空间中(或者甚至在全局上下文中)的模块的独立实例。动态命名空间支持模块模式的全部特征并附加直观和可读性强的优势。
在这里我们只是将命名空间作为参数传给自调用方法。变量id
是私有的,因为他并没有被分配给context
。
var myApp = {}; (function(context) { var id = 0; context.next = function() { return id++; }; context.reset = function() { id = 0; } })(myApp); window.console && console.log( myApp.next(), myApp.next(), myApp.reset(), myApp.next() ) //0, 1, undefined, 0
我们甚至可以把context
设置给全局对象(通过一个字的改变!)。这是库主们的巨大财富 – 他们可以将他们的特性包装在一个自调用函数中,然后让用户来决定它们是不是全局的(John Resig 在他写 JQuery 时就是一个这个理论的早期采用者)。
var myApp = {}; (function(context) { var id = 0; context.next = function() { return id++; }; context.reset = function() { id = 0; } })(this); window.console && console.log( next(), next(), reset(), next() ) //0, 1, undefined, 0
this
作为命名空间代理James Edwads 最近发布的一篇文章激起了我的兴趣。《My Favorite JavaScript Design Patter》 显然被很多评论者误解了,他们认为他可能也是借助于模块模式。这篇文章宣传了多种技术(可能导致了读者的迷惑),但是在它的核心部分是一点我已经修改并呈现为一个命名空间工具的很天才的东西。
这个模式的美就在于它仅仅是按照这个语言被设计的方式使用 – 不多不少、不投机也不取巧。此外因为命名空间是通过this
关键字(它在给定的执行上下文中是不变的)注入的,它不可能被意外修改。
var myApp = {}; (function() { var id = 0; this.next = function() { return id++; }; this.reset = function() { id = 0; } }).apply(myApp); window.console && console.log( myApp.next(), myApp.next(), myApp.reset(), myApp.next() ); //0, 1, undefined, 0
更棒的是,apply
(以及call
var subsys1 = {}, subsys2 = {}; var nextIdMod = function(startId) { var id = startId || 0; this.next = function() { return id++; }; this.reset = function() { id = 0; } }; nextIdMod.call(subsys1); nextIdMod.call(subsys2,1000); window.console && console.log( subsys1.next(), subsys1.next(), subsys2.next(), subsys1.reset(), subsys2.next(), subsys1.next() ) //0, 1, 1000, undefined, 1001, 0
this
を使用して兄弟プロパティを参照することで、将来のメンテナンスを容易にすることもできますが、名前空間内のメソッドの再割り当てを妨げるものがないため、これは少し危険です。 🎜nextIdMod(); window.console && console.log( next(), next(), reset(), next() ) //0, 1, undefined, 0
this
の値が「サプライズ」を引き起こす危険性はまだありますが、オブジェクト リテラル構造で定義されたオブジェクトは再割り当てされないと想定した方が安全です。 🎜//library code var protoQueryMooJo = function() { //everything } //user code var thirdParty = {}; protoQueryMooJo.apply(thirdParty);
モジュール パターン
を使用するようになりました。ロジックはメソッド ラッパー (通常は自己呼び出し) によってグローバル スコープから分離されており、モジュールのパブリック インターフェイスを表すオブジェクトを返します。このメソッドをすぐに呼び出して結果を名前空間変数に割り当てることで、モジュールの API をこの名前付き変数にロックします。さらに、戻り値に含まれない変数は常にプライベートのままとなり、それらを参照するパブリック メソッドに対してのみ表示されます。 🎜rrreee🎜上記のオブジェクト リテラルの例と同様、名前空間名は簡単に変更できますが、追加の利点があります。オブジェクト リテラルはクラス 4 です。これはすべて属性の割り当てに関するものであり、ロジックをサポートする余地はありません。さらに、すべてのプロパティを初期化する必要があり、オブジェクト間でプロパティ値を簡単に参照することはできません (そのため、たとえば、内部のクロージャは使用できません)。モジュール パターンには上記の制約がなく、追加のプライバシー上の利点が得られます。 🎜🎜動的名前空間🎜🎜このセクションを 名前空間インジェクション
と呼ぶこともできます。名前空間は、プロキシの 内部
の直接参照メソッド ラッパーによって表されます。これは、名前空間に割り当てられた戻り値をラップする必要がなくなったことを意味します。これにより、名前空間定義がより柔軟になり、独立した名前空間 (またはグローバル コンテキスト内) に存在するモジュールの複数の独立したインスタンスを持つことが可能になります。動的名前空間はモジュール パターンのすべての機能をサポートしており、直感的で読みやすいという利点も加えられています。 🎜id
は、context
に割り当てられていないため、プライベートです。 🎜rrreee🎜 context
をグローバル オブジェクトに設定することもできます (単語を 1 つ変更するだけです!)。これはライブラリの所有者にとって大きな資産です。ライブラリの機能を自己呼び出し関数でラップし、グローバルかどうかをユーザーに決定させることができます (John Resig は、JQuery を作成していたときにこの理論を早期に採用しました)。 🎜rrreeethis
を名前空間プロキシとして使用しますthis
キーワード (特定の実行コンテキスト内で不変) を介して挿入されるため、誤って変更されることはありません。 🎜rrreee🎜 さらに良いことに、apply
(および call
) API はコンテキストやパラメーターから自然に分離されているため、追加のパラメーターをモジュール作成者に渡すのは非常にクリーンです。次の例はこれを示し、複数の名前空間から独立してモジュールを実行する方法を示しています。 🎜rrreee🎜もちろん、グローバル ID ジェネレーターが必要な場合、それは非常に簡単です... 🎜nextIdMod(); window.console && console.log( next(), next(), reset(), next() ) //0, 1, undefined, 0
这个我们作为例子使用的 id 生成器工具并没有表现出这个模式的全部潜力。通过包裹一整个库和使用this
关键字作为命名空间的替身,我们使得用户在任何他们选择的上下文中运行这个库很轻松(包括全局上下文)。
//library code var protoQueryMooJo = function() { //everything } //user code var thirdParty = {}; protoQueryMooJo.apply(thirdParty);
我希望避免命名空间嵌套。它们很难追踪(对人和电脑都是)并且它们会让你的代码因为一些乱七八糟的东西变得很多。如 Peter Michaux 指出的,深度嵌套的命名空间可能是那些视图重新创建他们熟悉和热爱的长包链的老派 Java 开发者的遗产。
通过 .js 文件来固定一个单独的命名空间也是可以的(虽然只能通过命名空间注入或者直接分配每一个变量),不过你应该对依赖谨慎些。此外将命名空间绑定到文件上可以帮助读者更轻易弄清整个代码。
因为 JavaScript 并没有正式的命名空间结构,所以有很多自然形成的方法。这个调查只详细说明了其中的一部分,可能有更好的技术我没有发现。我很乐意知道它们。
以上就是JavaScript 中的命名空间详细介绍的内容,更多相关内容请关注PHP中文网(www.php.cn)!