3

モジュールに多くの関数が含まれている場合と非常によく似た、名前空間をマージできるいくつかの関数がimportあります (私は多数のコンビネータを持つ API を公開しています) var f = target.f;

function getNamespace(name, exports){
    var output='';
    for(var item in exports){
        output += 'var ' + item + ' = '+name+ '.'+item + ';';
    }
    return output;
}

と使用法:

var paco = require('./paco.js');

eval(paco.getNamespace('paco', paco));

// instead of paco.between(paco.start(),paco.content(),paco.end())
between(start(), content(), end())

質問:

eval を some 関数に「隠す」方法はありますか? vm.runInThisContextグローバル名前空間を変更したり、 を呼び出したりしたくありませんrequire。.

つまり、次のようなものが必要です

import('./paco'); 
// this should work like this
// var paco = require('./paco.js');       
// var between = paco.between;

ただし、global のミューテーションはなく、呼び出しスコープに eval はありません。

4

3 に答える 3

2

tl;dr: いいえ。

なぜこれが不可能なのかを理解するには、Node が舞台裏で何をしているのかを理解することが重要です。

test.js で関数を定義するとしましょう:

function foo() {
    var msg = 'Hello world';
    console.log(msg);
}

従来のブラウザー JavaScript では、その関数宣言をファイルに入れ、そのファイルを<script>タグでプルするだけfooで、グローバル スコープで宣言されていました。

ノードは、ファイルの場合とは異なる動作をrequire()します。

  1. まず、やや複雑な一連のルールに基づいて、ロードするファイルを正確に決定します。

  2. ファイルが JS テキスト (コンパイル済みの C++ アドオンではない) であると仮定すると、Node のモジュール ローダーはファイルの内容を取得するために呼び出します。 fs.readFileSync

  3. ソース テキストは無名関数でラップされます。test.js は実際には次のようになります。

    (function (exports, require, module, __filename, __dirname) {
    function foo() {
        var msg = 'Hello world';
        console.log(msg);
    }
    });
    

    これは、独自のコードを無名関数式でラップして、変数がブラウザーのグローバル スコープに漏れないようにしたことがある人なら誰でも知っているはずです。また、ノードの「魔法の」変数がどのように機能するかについても理解し始める必要があります。

  4. モジュール ローダーは、手順 3 のソース テキストをevals 1に渡し、結果の無名関数を呼び出して、新しいexportsオブジェクトを渡します。( を参照してくださいModule#_compile。)

    1 - Really 、呼び出し元のスコープにアクセスできないことを除いてvm.runInThisContext似ていますeval

  5. 無名ラッパー関数が戻った後、 の値がmodule.exports内部的にキャッシュされ、 によって返されrequireます。require()(キャッシュされた値を返すための後続の呼び出し。)

ご覧のとおり、Node はファイルのソース コードを無名関数でラップするだけで「モジュール」を実装します。したがって、JavaScript は関数の実行コンテキスト(関数のローカル変数のコレクション)への直接アクセスを提供しないため、関数をモジュールに「インポート」することはできません。

つまり、関数のローカル変数をループする方法はありません。また、オブジェクトのプロパティでできるように、任意の名前でローカル変数を作成する方法もありません。

たとえば、オブジェクトを使用すると、次のようなことができます。

var obj = { key: 'value' };
for (var k in obj) ...
obj[propertyNameDeterminedAtRuntime] = someValue;

しかし、関数のローカル変数を表すオブジェクトはありません。これは、オブジェクトのプロパティ (exportsモジュールの など) を関数のローカル スコープにコピーするために必要です。

あなたが行ったことは、を使用して現在のスコープ内でコードを生成することですeval。生成されたコードは、キーワードを使用してローカル変数を宣言し、それが呼び出されvarたスコープに挿入されます。eval

呼び出しをモジュールの外に移動する方法はありません。移動するevalと、挿入されたコードが別のスコープに挿入されるためです。JavaScript には静的スコープがあるため、関数をレキシカルに含むスコープにしかアクセスできないことに注意してください。

もう 1 つの回避策は を使用することですwithが、使用は避けてくださいwith

with (require('./paco.js')) {
    between(start(), content(), end())
}

with次の 2 つの理由から使用しないでください。

  1. V8 は名前検索の最適化を実行できないため、パフォーマンスが完全低下します。
  2. 非推奨であり、厳密モードでは禁止されています。

正直に言うと、 でトリッキーなことをするのではなくeval、将来のメンテナーに有利に働き、モジュールのエクスポートをローカル変数に割り当てるという標準的な方法に従うことをお勧めします。

頻繁に入力する場合は、1 文字の名前にします (または、より優れたエディターを使用します)。

于 2013-08-19T15:41:18.743 に答える
0

いいえ。外部モジュールからローカル スコープを変更することはできません。その理由は、 eval が外部モジュールで呼び出されると、そのコンテキストは、モジュールを必要とするスコープではなく、外部モジュールになります。

さらに、vm.runInThisContext はローカル スコープにアクセスできないため、どちらも役に立ちません。

于 2013-08-19T15:18:34.783 に答える
0

この回答によると、node.js標準モジュールのグローバル変数? globalブラウザと同じオブジェクトがありますwindow。したがって、そのオブジェクトにキーを追加できます

function getNamespace(exports) {
    for(var item in exports){
        global[item] = exports[item];
    }
}

そしてそれを次のように使用します:

paco.getNamespace(paco);

eval はまったく必要ありません。

于 2013-08-19T13:40:37.923 に答える