Node.jsでスクリプトが実行されているかどうかを確認するにはどうすればよいですか?


159

Node.jsスクリプトに必要なスクリプトがあります。JavaScriptエンジンを独立させておきたいのですが。

たとえば、exports.x = y;Node.jsで実行されている場合にのみ実行したいと思います。このテストを実行するにはどうすればよいですか?


この質問を投稿したとき、Node.jsモジュールの機能がCommonJSに基づいていることを知りませんでした。

私が挙げた具体的な例では、より正確な質問は次のようになります。

CommonJSモジュールとして必要であるかどうかをスクリプトで確認するにはどうすればよいですか?


3
私は考えてきたんなぜあなたはこれをやろうとしているのが、経験則として、あなたではなく、エンジンの検出を特徴検出を使用する必要があります。quirksmode.org/js/support.html
Quentin

4
これは実際には機能検出を実装する方法の要求ですが、質問はそれ自体をうまく説明していません。
モノクローム2013年

私自身が使用するためのライブラリを公開しました。これがnpmjs.com/package/detect-is-nodeの
abhirathore2006


質問とほとんどの回答の1つの問題は、ブラウザーまたはNode.jsの2つの可能性しかないという仮定です。Oracle Java Nashornの場合のように、ブラウザでもNode.jsでもない可能性があります。JDKがインストールされている場合、jjsコマンドを使用してスクリプトを実行できます。ただし、NashornとNode.jsの間には多くの違いがあるため、想定をすることはできません。そして、誰が未来がもたらすかもしれないオプションを知っていますか?特徴の検出が必要です。

回答:


80

CommonJSのサポートを探すことで、これはUnderscore.jsライブラリが行う方法です。

編集:更新された質問へ:

(function () {

    // Establish the root object, `window` in the browser, or `global` on the server.
    var root = this; 

    // Create a reference to this
    var _ = new Object();

    var isNode = false;

    // Export the Underscore object for **CommonJS**, with backwards-compatibility
    // for the old `require()` API. If we're not in CommonJS, add `_` to the
    // global object.
    if (typeof module !== 'undefined' && module.exports) {
            module.exports = _;
            root._ = _;
            isNode = true;
    } else {
            root._ = _;
    }
})();

ここでの例は、モジュールパターンを保持しています。


45
これにより、ブラウザがサポートするCommonJSサポートが検出されます。
mikemaccana 2012

7
ここに問題があり、釘打ち機は「釘付け」しました。私はブラウザでCommonJSを試しており、使用しているモジュールローダーはmodule.exportsを定義しているため、このソリューションではノードにいると誤って通知されます。
マークメルビル

1
@MarkMelville間違いなく、これはまさにOPが求めていることなので、問題ではありません。
ロス

13
私の言い回しが悪い。つまり、このソリューションには問題があります。OPはそれを受け入れたかもしれませんが、私は受け入れません。
Mark Melville、

7
これはおそらく、与えられた最良の答えではありません。
user3751385 14年

107

すべてのWebサイトが同じ変数を簡単に宣言できるため、Node.jsで実行を検出する信頼できる方法はありません。windowただし、Node.jsにはデフォルトでオブジェクトがないため、他の方法で回避して、内部で実行されているかどうかを確認できます。ブラウザ。

これは、ブラウザとNode.jsの両方で機能するライブラリに使用するものです。

if (typeof window === 'undefined') {
    exports.foo = {};

} else {
    window.foo = {};
}

windowNode.jsで定義されている場合でも爆発する可能性がありますが、明示的に除外するかオブジェクトのプロパティを設定する必要があるため、誰かがこれを行う正当な理由はありません。varglobal

編集

スクリプトがCommonJSモジュールとして必要であるかどうかを検出するために、これも簡単ではありません。commonJSが指定しているのは、A:モジュールが関数の呼び出しを介して含まれること、requireおよびB:モジュールがexportsオブジェクトのプロパティを介して物事をエクスポートすることだけです。これがどのように実装されるかは、基になるシステムに任されています。Node.jsは、モジュールのコンテンツを無名関数でラップします。

function (exports, require, module, __filename, __dirname) { 

参照:https : //github.com/ry/node/blob/master/src/node.js#L325

しかし、いくつかのクレイジーなものを介してそれを検出しようとせずarguments.callee.toString()、代わりにブラウザをチェックする上記のサンプルコードを使用してください。Node.jsはよりクリーンな環境であるため、windowそこで宣言されることはほとんどありません。


2
「Node.jsはよりクリーンな環境なので、そこでウィンドウが宣言される可能性はほとんどありません。」:まあ、node.js + JSDOMによってエミュレートされたブラウザーでスクリプトが実行されているかどうかを調べる方法を探してここに来ましたまたはプレーンブラウザで...理由は、setTimeoutを使用してURLの場所を確認する無限ループがあるためです。これは、ブラウザでは問題ありませんが、node.jsスクリプトが永久に実行され続けるので、ウィンドウがある可能性があります。結局のところnode.jsスクリプトで:)
EricBréchemier

1
@Ericそれがグローバルスコープに存在することを強く疑うのでwindow、モジュールの最初の行のように何かをインポートしない限り、問題は発生しません。また、匿名関数を実行し、チェックできる[[Class]]のがthis下の「クラス」を参照してください。(唯一の非strictモードで動作します)その中:bonsaiden.github.com/JavaScript-Garden/#typeof
イヴォウェッツェル

1
私の問題はOPとは少し異なります。スクリプトは必要ありません。JSDOMによって、エミュレートされたウィンドウがグローバルコンテキストとして読み込まれます。通常のモジュールとは異なるコンテキストで、node.js + V8によって実行されます。
エリックブレケミエ

1
おそらく...私は別の方向に行きました:1)無限ループの作成を回避するためにonhashchange(ウィンドウ内の「onhashchange」)のサポートを検出します2)メインnode.jsスクリプトでエミュレートされたウィンドウにonhashchangeプロパティを設定してサポートをシミュレートします。
エリックブレシェミエ

1
typeof self === 'object'typeof window === 'undefined'Webワーカーのスコープで失敗するため、より安全かもしれません。
ルイス

45

私は現在、誤解を招く機能検出のため、Electronのノード環境を認識していないノードの誤った検出に遭遇しました。次のソリューションは、プロセス環境を明示的に識別します。


Node.jsのみを識別する

(typeof process !== 'undefined') && (process.release.name === 'node')

process.release「現在の[Node-] releaseに関連するメタデータ」が含まれているため、Node-processで実行しているかどうかがわかります。

io.jsの生成後、の値process.release.nameも同様になる可能性がありますio.jsprocess-docを参照)。ノード対応環境を適切に検出するには、次のように確認する必要があります。

ノード(> = 3.0.0)またはio.jsを識別します

(typeof process !== 'undefined') &&
(process.release.name.search(/node|io.js/) !== -1)

このステートメントは、ノード5.5.0、エレクトロン0.36.9(ノード5.1.1を含む)、およびChrome 48.0.2564.116でテストされました。

ノード(> = 0.10.0)またはio.jsを識別します

(typeof process !== 'undefined') &&
(typeof process.versions.node !== 'undefined')

@daluegeのコメントは、より一般的な証明について考えるきっかけになりました。これはNode.js> = 0.10から機能するはずです。以前のバージョンの一意の識別子が見つかりませんでした。


Ps:OPは別の質問への回答を探していましたが、質問がここに導いてくれたので、ここでその回答を投稿しています。


2
これは断然最も信頼できるアプローチのようです、ありがとう。バージョン3.0.0以上でのみ機能します。
フィリップ、2016年

@daluege-インスピレーションをありがとう。残念ながら、0.10未満の証拠は見つかりませんでした。
Florian Breisch、2016年

3
私はWebPACKの反応し、使用が見出さprocess及びprocess.versionIは、のための余分なチェックを加えて、バンドル内に存在するクライアント側で定義されていないが、サーバ側の値としてノードバージョンを有しているprocess.versionprocess.release.node
アーロン

@アーロン:そのヒントをありがとう。process.version変数の定義を見つけることができませんでした(react、webpack、またはreact-webpack)。答えに追加するためにバージョン変数が定義されているヒントを教えてください。ノードへのrelease.node制約に依存> = 3.xx
Florian Breisch '20

2
ワンライナーで安全:function isNodejs() { return typeof "process" !== "undefined" && process && process.versions && process.versions.node; }
brillout 2018年

25

コードが実行されている環境を把握しようとする際の問題は、オブジェクトを変更および宣言できるため、どのオブジェクトが環境にネイティブで、どのオブジェクトがプログラムによって変更されたかを把握することがほぼ不可能になることです。

ただし、現在の環境を確認するために使用できるいくつかのトリックがあります。

下線ライブラリで使用されている一般に受け入れられているソリューションから始めましょう:

typeof module !== 'undefined' && module.exports

require関数が呼び出されると、thisオブジェクトが空のオブジェクトにリセットされ、再定義さmoduleれるので、外部の改ざんを心配する必要がないので、この手法は実際にはサーバー側にとって完全に適切です。コードがで読み込まれている限り、require安全です。

ただし、これはブラウザ上ではバラバラになっており、誰でも簡単に定義moduleして、探しているオブジェクトのように見せかけることができます。一方でこれはあなたが望む振る舞いかもしれませんが、それはまた、ライブラリユーザーがグローバルスコープで使用できる変数を決定します。多分誰かが別の用途のためにmoduleそのexports中にある名前を持つ変数を使いたいと思うでしょう。ありそうもないことですが、別の環境がその変数名を使用しているという理由だけで、他の誰かが使用できる変数を判断するのは誰ですか?

ただし、トリックは、スクリプトがグローバルスコープで読み込まれていると想定している場合(スクリプトタグを介して読み込まれている場合)、変数が外部クロージャーで予約できないことです。これは、ブラウザーで許可されていないためです。 。ノードで覚えておいてください、thisオブジェクトは空のオブジェクトmoduleですが、変数はまだ利用可能です。それは外側のクロージャーで宣言されているからです。アンダースコアのチェックを修正するには、追加のチェックを追加します。

this.module !== module

これにより、誰かmoduleがブラウザのグローバルスコープで宣言すると、thisオブジェクトに配置されます。これはthis.module、がモジュールと同じオブジェクトになるため、テストが失敗する原因になります。ノードにthis.moduleは存在せずmodule、外側のクロージャー内に存在するため、同等ではないため、テストは成功します。

したがって、最終的なテストは次のとおりです。

typeof module !== 'undefined' && this.module !== module

注:これにより、module変数をグローバルスコープで自由に使用できるようになりますが、新しいクロージャーを作成しmoduleてその中で宣言し、そのクロージャー内でスクリプトをロードすることにより、ブラウザーでこれをバイパスすることも可能です。その時点で、ユーザーはノード環境を完全に複製しており、うまくいけば、自分が何をしているかを理解しており、ノードスタイルで必要なことを実行しようとしています。コードがスクリプトタグで呼び出された場合でも、新しい外部クロージャーからは安全です。


2
うわー、ワンライナーの各部分の背後にある理論的根拠を明確に説明してくれてありがとう。
Jon Coombs

Cannot read property 'module' of undefinedこれは、たとえば
モカテストで

20

以下は、故意に、明示的に妨害されない限り、ブラウザで機能します。

if(typeof process === 'object' && process + '' === '[object process]'){
    // is node
}
else{
    // not node
}

バム。


4
var process = {toString:function(){return '[object process]'; }};
Nick Desaulniers 2014

1
process+''代わりに使用する理由はありますprocess.toString()か?
有害な2015

3
ほとんど。代わりにこれを使用してください:Object.prototype.toString.call(process)
sospedra

2
これがこの質問に対する最良の答えです。
loretoparisi 2016年

3
@harmic:var process = null;2番目のケースが失敗する原因になります。JavascriptとJavaの両方で、式'' + xは、厄介な場合、前者が生成する場合、または後者がエラーをスローするx.toString()場合を除いて、同じ結果を生成します。x"null""undefined"
joeytwiddle

17

同様にそれを行うにはかなりクールな方法があります:

const isBrowser = this.window === this;

これは、ブラウザではグローバルな「this」変数に「ウィンドウ」と呼ばれる自己参照があるため機能します。この自己参照はノードに存在しません。

  • ブラウザでは、「this」は「window」と呼ばれるグローバルオブジェクトへの参照です。
  • ノードの 'this'はmodule.exportsオブジェクトへの参照です。
    • 「これ」は「グローバル」と呼ばれるノードグローバルオブジェクトへの参照ではありません
    • 「これ」は、モジュール変数宣言スペースへの参照ではありません

上記の推奨ブラウザチェックを解除するには、次のようなことを行う必要があります。

this.window = this;

チェックを実行する前。


なぜ単純ではないのですconst isBrowser = this.window !== undefinedか?そしてノードで、理論的には私が行うことができますthis.window = this解決を欺くために。
タイラーロング

11

さらに別の環境検出

(意味:ここでの答えのほとんどは問題ありません。)

function isNode() {
    return typeof global === 'object'
        && String(global) === '[object global]'
        && typeof process === 'object'
        && String(process) === '[object process]'
        && global === global.GLOBAL // circular ref
        // process.release.name cannot be altered, unlike process.title
        && /node|io\.js/.test(process.release.name)
        && typeof setImmediate === 'function'
        && setImmediate.length === 4
        && typeof __dirname === 'string'
        && Should I go on ?..
}

少し偏執的ですね?より多くのグローバルをチェックすることで、これをより詳細にすることができます

しかし、しないでください。

上記のこれらすべてはとにかく偽造/シミュレーションすることができます。

たとえば、globalオブジェクトを偽造するには:

global = {
    toString: function () {
        return '[object global]';
    },
    GLOBAL: global,
    setImmediate: function (a, b, c, d) {}
 };
 setImmediate = function (a, b, c, d) {};
 ...

これはノードの元のグローバルオブジェクトにアタッチされませんがwindow、ブラウザでオブジェクトにアタッチされます。したがって、ブラウザ内のノード環境にいることを意味します。

人生は短いです!

私たちの環境が偽物であるかどうかを気にしますか?愚かな開発者globalがグローバルスコープで呼び出されるグローバル変数を宣言すると発生します。または、悪意のある開発者がなんとかして環境にコードを挿入します。

これをキャッチするとコードが実行されない場合がありますが、アプリの他の多くの依存関係がこれに巻き込まれる可能性があります。したがって、最終的にはコードが壊れます。コードが十分であれば、他の人が行った可能性のあるすべての愚かな間違いを気にしないでください。

だから何?

2つの環境をターゲットにする場合:ブラウザーとノード。
"use strict"; windowまたは単にを確認するかglobal、そして、あなたのコードがこれらの環境のみをサポートすることをドキュメントで明確に示してください。それでおしまい!

var isBrowser = typeof window !== 'undefined'
    && ({}).toString.call(window) === '[object Window]';

var isNode = typeof global !== "undefined" 
    && ({}).toString.call(global) === '[object global]';

あなたのユースケースで可能であれば; 環境検出の代わりに; try / catchブロック内で同期機能検出を行います。(これらの実行には数ミリ秒かかります)。

例えば

function isPromiseSupported() {
    var supported = false;
    try {
        var p = new Promise(function (res, rej) {});
        supported = true;
    } catch (e) {}
    return supported;
}

9

提案されたソリューションのほとんどは、実際に偽造することができます。堅牢な方法は、Classを使用してグローバルオブジェクトの内部プロパティをチェックすることObject.prototype.toStringです。JavaScriptで内部クラスを偽造することはできません。

var isNode = 
    typeof global !== "undefined" && 
    {}.toString.call(global) == '[object global]';

2
これはbrowserifyの下で真実に戻ります。
2014年

1
あなたはそれをテストしましたか?browserifyがオブジェクトの内部クラスをどのように変更できるかわかりません。これには、JavaScript VMのコードを変更するか、上書きする必要がありますが、Object.prototype.toStringこれは実際に悪い習慣です。
Fabian Jakobs、2014年

私はそれをテストしました。ここでbrowserifyが何をするかです: var global=typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {};
Vanuan

ご覧のとおり、Chromeでは({}.toString.call(window))同等"[object global]"です。
Vanuan 2014年

2
奇妙なのは、window.toString()プロデュースする"[object Window]"
バヌアン2014年



4

これが上記のものの私のバリエーションです:

(function(publish) {
    "use strict";

    function House(no) {
        this.no = no;
    };

    House.prototype.toString = function() {
        return "House #"+this.no;
    };

    publish(House);

})((typeof module == 'undefined' || (typeof window != 'undefined' && this == window))
    ? function(a) {this["House"] = a;}
    : function(a) {module.exports = a;});

これを使用するには、最後の2行目の「House」をモジュールの名前をブラウザに表示するように変更し、モジュールの値を通常のように公開します(通常はコンストラクタまたはオブジェクトリテラル) )。

ブラウザでは、グローバルオブジェクトはwindowであり、それ自体への参照を持っています(== windowであるwindow.windowがあります)。これは、ブラウザーを使用している場合、またはブラウザーを使用していると信じ込ませたい環境でない限り、発生する可能性は低いようです。他のすべての場合では、宣言されたグローバル「モジュール」変数がある場合、それを使用します。それ以外の場合は、グローバルオブジェクトを使用します。


4

私はprocessそのようにnode.jsをチェックするために使用しています

if (typeof(process) !== 'undefined' && process.version === 'v0.9.9') {
  console.log('You are running Node.js');
} else {
  // check for browser
}

または

if (typeof(process) !== 'undefined' && process.title === 'node') {
  console.log('You are running Node.js');
} else {
  // check for browser
}

ここに文書化


2
process.title変更可能
Ben Barkay 2013

次に、変更したタイトルを確認します。または、process.versionを使用してください
Chris

ライブラリを書いている場合(そうする必要があります)、タイトルがどうなるか期待することはできません
Ben Barkay

3

この記事の執筆時点では、この回答はJavaScriptの非常に新しい機能を活用しているため、「近日公開」の選択肢となっています。

const runtime = globalThis.process?.release?.name || 'not node'
console.log(runtime)

runtime値がどちらかになりますnodenot node

前述のように、これはいくつかの新しいJavaScript機能に依存しています。globalThisECMAScript 2020仕様の最終的な機能です。オプションのチェーン/ヌルの合体(の?一部globalThis.process?.release?.name)は、Chrome 80に同梱されているV8エンジンでサポートされています。2020年4月8日以降、このコードはブラウザーでは機能しますが、ノード13ブランチが使用するため、ノードでは機能しません。 V8 7.9.xxx。ノード14(2020年4月21日リリース予定)ではV8 8.x +を使用することになっています。

このアプローチには、現在の制限の健全な線量が付属しています。しかしながら; ブラウザ/ノードがリリースされるペースは、最終的に信頼できるワンライナーになるでしょう。


1
これは受け入れられる答えになるはずです!誰もがノード14を使用する必要があります
シート

2

Node.jsにはprocessオブジェクトがあります。そのため、作成processする他のスクリプトがない限り、それを使用して、コードがノードで実行されるかどうかを判断できます。

var isOnNodeJs = false;
if(typeof process != "undefined") {
  isOnNodeJs = true;
}

if(isOnNodeJs){
  console.log("you are running under node.js");
}
else {
  console.log("you are NOT running under node.js");
}

2

これは、サーバーサイドとクライアントサイドのjavascript間の互換性を保証するかなり安全で簡単な方法であり、browserify、RequireJS、またはCommonJSに含まれるクライアントサイドでも機能します。

(function(){

  // `this` now refers to `global` if we're in NodeJS
  // or `window` if we're in the browser.

}).call(function(){
  return (typeof module !== "undefined" &&
    module.exports &&
    typeof window === 'undefined') ?
    global : window;
}())

1

編集:更新された質問について:「スクリプトは、commonjsモジュールとして必要であったかどうかをどのようにして知ることができますか?」できないと思います。仕様ではモジュールに提供する必要があるためexports、がオブジェクト(if (typeof exports === "object"))かどうかを確認できますが、... がオブジェクトであることだけがわかります。:-)exports


元の答え:

(私は確かにいくつかのNodeJS固有のシンボルがありますよんEventEmitter多分、 何、あなたが使用する必要がありますrequire。下記を参照のイベントモジュールを取得するには、むしろ(あなたがチェックすることができること)が、ダビデは言ったように、理想的にあなたは特徴を検出方がいいでしょう環境よりも)そうすることが理にかなっている場合。

更新:おそらく次のようなものです:

if (typeof require === "function"
    && typeof Buffer === "function"
    && typeof Buffer.byteLength === "function"
    && typeof Buffer.prototype !== "undefined"
    && typeof Buffer.prototype.write === "function") {

ただし、これは、requireNodeJSと非常によく似た環境にいることを示していますBuffer。:-)


それでも、Webサイトにそのようなものをすべてセットアップすることでそれを壊すことができます...それはやり過ぎです;)ノード環境がクリーンであるため、ブラウザーにあることを確認することは簡単です。
Ivo Wetzel 2010年

1
@Ivo:はい、最後の文章をご覧ください。windowNodeJSアプリケーション内で変数を定義することで、チェックを簡単に破ることができます。:-)
TJ Crowder、

1
@Ivo:誰かがNodeJSモジュールで定義されていてもまったく驚かないwindowのでwindow、グローバルオブジェクトであることに依存し、そのコードを変更したくないコードを含めることができます。はそれをしません、あなたはしませんが、誰かが持っているに違いない。:-)または、それらはwindow完全に別のものを意味するために使用されました。
TJクラウダー2010年

1
@Ivo:yuiblog.com/blog/2010/04/09/…は、node.jsでウィンドウオブジェクトが定義される理由の1つです
slebetman '19

1
@TJCrowdertypeof process !== "undefined" && process.title === "node"
レイノス

0
const isNode =
  typeof process !== 'undefined' &&
  process.versions != null &&
  process.versions.node != null;


-1

node.jsのソースを取得して、のような変数を定義するように変更しますrunningOnNodeJS。コードでその変数を確認します。

node.jsの独自のプライベートバージョンを作成できない場合は、プロジェクトで機能リクエストを開きます。実行しているnode.jsのバージョンを提供する変数を定義するよう依頼してください。次に、その変数を確認します。


1
これでも彼の(基本的には解決できない)問題は解決されません。ブラウザでそのような変数を作成できます。より良い方法は、スクリプトがwindowグローバルを作成しないようにすることです。私はそのグローバルに機能リクエストを提出すると思います。
Ivo Wetzel 2010年

@Ivo:jsdom(github.com/tmpvar/jsdom)を使用して、YUIやjQueryなどの使い慣れたライブラリを使用してサーバー側のdom操作を行うコードを壊すのは、悪い考えです。そして、現在これを行うコードが本番環境にあります。
slebetman、2010年

@slebetmanいいえ、jsdomを壊しません。私はglobalについて話していますが、varステートメントではないglobalのように、そこにあるサンプルコードはvarステートメントを使用しています。それをグローバル名前空間に漏らしただけの人は、自己完結型モジュールの概念を
理解

@Ivoは暴力的で、ケーキを食べる能力が必要だと言っているようなものです。モジュール間で機能するライブラリを実現するには、グローバル名前空間を散らかす必要あります。または、すべてを1つのモジュールにまとめることもできますが、それでは何が重要なのでしょうか。
Ben Barkay 2013

-1

非常に古い投稿ですが、requireステートメントをtryでラップして解決しました-catch

try {
     var fs = require('fs')
} catch(e) {
     alert('you are not in node !!!')
}

2
これは真実ではありません。browserifyを使用して「nodeish」require()コールを使用できます
fat
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.