グローバルを非表示にする理由は異なる場合があります。美しさ、セキュリティ上の理由、コード分析の難しさなどです。 ユーザーは、自分が破ることができないイベントを使用してコードを操作します。他に何も必要ありません。
最も明白な方法は、他のオブジェクトを受動的にエクスポートする単一の名前空間を作成し、最後にjQueryと$を削除することです。
アセンブリ後、コードは次のようになります。
(function(window, undefined){ // include ./js/YourNamespace.js var YourNamespace = (function () { // - return {}; }()); // include ./js/YourNamespace/SomeObject.js YourNamespace.SomeObject = (function () { // - return function () { }; }()); // Cleanup delete window.$; delete window.jQuery; }(window));
これは理想的ですが、大抵はそうではありません。 あなたのコードを見て、そうですか?
アンダーカットは、単一のグローバル変数なしで任意のコードを収集できる汎用ソリューションです。
コンセプトアクティブ/パッシブインポート/エクスポート
アクティブなエクスポート
(function (window) { var ModuleA = {}; // Export window.ModuleA = ModuleA; }(window));
ModuleAモジュールはアクティブにエクスポートされます
パッシブエクスポート
var ModuleC = (function (window) { var ModuleC = {}; // Export return ModuleC; }(window));
ModuleCモジュールは受動的にエクスポートされます
アクティブなインポート
(function (window, $) { console.log($); }(this, jQuery));
JQueryモジュールはアクティブにインポートされます
パッシブインポート
(function (window) { console.log(ModuleC); }(this));
ModuleCモジュールは受動的にインポートされます
組立
とにかくインポート/エクスポートするモジュールがいくつかあります。 それぞれが使用するモジュールを知っています。 各モジュールをクロージャでラップして、使用するすべてのオブジェクトのリストを転送できます。 確かに、小さな問題があります:モジュールAがモジュールBを使用し、モジュールBがモジュールAを使用し、それらがすべて直列に接続されている場合、ループにラップされたモジュールAを接続すると、ReferenceErrorエラーが発生します-モジュールBはまだありません この問題は、モジュールBをロードした後にオブジェクトを回路Aに転送することで解決します。モジュールが受動的にエクスポートされ、回路でラップすると、グローバル変数は消えます。 この問題は、ローカル変数をグローバルコンテキストに転送することで解決されます。
スクリプトの初期化中に作成したグローバル変数のリストを知っています-グローバルをクリアするには、deleteを使用してそれらを削除します。
例
ランダムにインポート/エクスポートするjQueryと3つのモジュールで構成されるサンプルプロジェクトを見てみましょう。
モジュールリスト
// Uses ModuleC, $ (function (window) { var ModuleA = { a: 'ModuleA.a', b: 2, d: function () { console.log(ModuleC.c === 'ModuleC.c'); console.log(typeof $ === 'function'); } }; // Export window.ModuleA = ModuleA; }(window));
// Uses ModuleA var ModuleC = (function (window) { var ModuleC = { a: 1, b: 2, c: 'ModuleC.c', d: function () { console.log(ModuleA.a === 'ModuleA.a'); } }; // Export return ModuleC; }(window));
// Uses ModuleA, ModuleC ModuleA.c(); window.setTimeout(function () { ModuleC.d(); ModuleA.d(); }, 0);
モジュールは、jQuery個別、ModuleA + ModuleC + ModuleDの順序で接続されます。 モジュールAはモジュールCに依存し、モジュールCはモジュールAに依存します(上記の競合)。 モジュールAは積極的にエクスポートし、モジュールCはそれ自体を受動的にエクスポートし、モジュールDは何もエクスポートしません。
集める
// (function(window, undefined){ // var Medium = { wait: function (varName, callback) {/* - */}, ready: function (varName, varValue) {/* - */} }; // A, (function (ModuleC, $) { // .. ModuleC , Medium.wait('ModuleC', function (value) {ModuleC = value;}); (function (window) { var ModuleA = { a: 'ModuleA.a', b: 2, d: function () { console.log(ModuleC.c === 'ModuleC.c'); console.log(typeof $ === 'function'); } }; // Export window.ModuleA = ModuleA; }(window)); } (undefined, $)); // C, (function (ModuleA) { var ModuleC = (function (window) { var ModuleC = { a: 1, b: 2, c: 'ModuleC.c', d: function () { console.log(ModuleA.a === 'ModuleA.a'); } }; // Export return ModuleC; }(window)); // .. ModuleC , , window.ModuleC = ModuleC; // C - Medium.ready('ModuleC', ModuleC); } (ModuleA)); // , (function (ModuleA,Module) { // Uses ModuleA, ModuleC ModuleA.c(); window.setTimeout(function () { ModuleC.d(); ModuleA.d(); }, 0); } (ModuleA,Module)); // - try { delete window.$; delete window.jQuery; delete window.ModuleA; delete window.Module; } catch (e){ // IE window.$ = undefined; window.jQuery = undefined; window.ModuleA = undefined; window.Module = undefined; } // ! }(window));
変数キャプチャの問題
このソリューションは、コードを分析し、不必要な変数のグローバルコンテキストをクリアすることを難しくするだけです(美しさのため)。
スクリプト(通常のユーザースクリプトまたは拡張機能)を接続する前に組み込まれたスクリプトが、watch、__ defineSetter __、ES5 setを使用して、場合によっては最小setIntervalを介して削除する前に、ModuleAおよびModuleオブジェクトをインターセプトできることは秘密ではありません。
この問題には3つの解決策があります。
1.アクティブなエクスポートを使用しないでください(キャプチャの機会はゼロです。コードを変更する必要があります)
2.グローバル変数にランダムな名前を使用します(キャプチャチャンスはゼロになる傾向があるため、コードを変更する必要があります)
3.組み立てる前にオブザーバーとタイマーを削除します(キャプチャチャンスはゼロになります)
方法3を使用して、アセンブリを変更します。
// (function(window, undefined){ // __defineSetter__, ES5 set, watch window.unwatch && window.unwatch('ModuleA'); window.unwatch && window.unwatch('Module'); try { delete window.ModuleA; delete window.Module; } catch (e){ // IE window.ModuleA = undefined; window.Module = undefined; } // var maxIntervalId = window.setInterval(function (){}, 1e10); var maxTimeoutlId = window.setTimeout(function (){}, 1e10); while (maxIntervalId--) { window.clearInterval(maxIntervalId); } while (maxTimeoutlId--) { window.clearTimeout(maxTimeoutlId); } // // ... }(window));
これで、JavaScript Ninjaの最も洗練されたテクニック-「グローバル変数の非表示」をマスターできました!
例外
このメソッドは、特にグローバルに散らばる場合、onsmth = "..."を介してイベント呼び出しを使用する場合には役立ちません。つまり、 不十分な動作;)グローバルにオブジェクトがない場合、メソッドは機能しません。
Ninja JavaScript Builder-Ninjs
このアセンブリメソッドの実装を書く必要がないように、私はエース-Ninjsを書きました。 これは、上記の方法を使用したJavaScriptプロジェクトビルダーです。 ビルダーは、彼の名前を正当化します-忍者として、彼はいつの間にか自分の仕事をし、トラックを整理します。
プロジェクトはgithubにありますgithub.com/azproduction/ninjs
Node.jsを使用してビルドします。
npmにはまだ登録されていませんが、確実に登録されます。
彼が変数のキャプチャを防ぐことができるまで-になります。
使用する
Ninjsの使用は非常に簡単です-モジュールがインポート/エクスポートする変数とそれらが存在する場所を知る必要があります。 すべてのNinjs依存関係が解決されます。
var ninjs = new (require('../Ninjs.js').Ninjs); ninjs .add({ // ModuleA file: './files/ModuleA.js', // ModuleC ModuleB imports: ['ModuleC', 'ModuleB'], // ModuleA exports: 'ModuleA' }) .add({ // ModuleB file: './files/ModuleB.js', // ModuleA jQuery imports: 'ModuleA', // ModuleB exports: 'ModuleB' }) .add({ // ModuleD file: './files/ModuleD.js', // ModuleA, ModuleB and ModuleC imports: ['ModuleA', 'ModuleB', 'ModuleC'] // }) .add({ // ModuleC file: './files/ModuleC.js', // ModuleA, ModuleB imports: ['ModuleA', 'ModuleB'], // ModuleC exports: 'ModuleC', // ModuleC , forceExports: 'ModuleC' }) // .cleanup('ModuleA', 'ModuleB', 'ModuleC', '$', 'jQuery') // STDOUT .print(true); // , // .print(false);
サンプルコードとモジュールコードはこちらgithub.com/azproduction/ninjs/tree/master/examples
お楽しみに。 提案、希望、批判を歓迎します!
PS私はそれをNinjsのロゴに変えるためにNinjaアイコンを探しています。