web-dev-qa-db-ja.com

環境検出:node.jsまたはブラウザー

クライアント側とサーバー側の両方で動作する必要があるJSアプリを開発しています(ブラウザのJavascriptとNode.jsで)。コードの一部を再利用できるようにしたいと思います。両側に使用されます。

windowはブラウザでのみアクセス可能な変数であり、ノードではglobalであることがわかったため、どの環境でコードが実行されているかを検出できます(スクリプトがwindow変数)

それらは2つの問題です。

  1. コードが実行されているブラウザを検出する方法。たとえば、このコードはOKです。 (このコードはインラインです。つまり、両方の環境で再利用されるグローバルコードに囲まれています)

    if window?
        totalPath= "../examples/#{path}"
    else
        totalPath= "../../examples/#{path}"
    
  2. 両方の環境でグローバル変数を使用するにはどうすればよいですか?今、私は次のことをしていますが、これは本当に正しいとは感じていません。

    if window?
        window.DocUtils = {}
        window.docX = []
        window.docXData= []
    else
        global.DocUtils= {}
        global.docX = []
        global.docXData = []
    
46
edi9999

注:この質問には2つの部分がありましたが、タイトルが「環境検出:node.jsまたはブラウザ」だったため、最初にこの部分に行きます。それに対する答え。別の質問があるかもしれません。

JavaScriptの変数は内部スコープによって再定義できるため、たとえば、node.js jsdomモジュールを使用している場合、環境がprocess、globalまたはwindowという名前の変数を簡単に作成できないと仮定すると、 APIの使用例has

_var window = doc.defaultView;
_

その後、window変数の存在に基づいて環境を検出すると、そのスコープで実行されているモジュールによって体系的に失敗します。同じロジックを使用すると、ブラウザーベースのコードはglobalまたはprocessを簡単に上書きできます。これらの環境では予約変数ではないためです。

幸いなことに、グローバルスコープを要求し、それが何であるかをテストする方法があります-new Function()コンストラクターを使用して新しい関数を作成する場合、thisの実行スコープはグローバルスコープにバインドされ、グローバルスコープを期待値と直接比較できます。 *)

したがって、グローバルスコープが「ウィンドウ」であるかどうかをチェックする関数を作成するには

_var isBrowser=new Function("try {return this===window;}catch(e){ return false;}");

// tests if global scope is binded to window
if(isBrowser()) console.log("running under browser");
_

そして、グローバルSOPが「グローバル」にバインドされているかどうかをテストする機能は

_var isNode=new Function("try {return this===global;}catch(e){return false;}");

// tests if global scope is binded to "global"
if(isNode()) console.log("running under node.js");
_

try ... catch -partは、変数が定義されていない場合、falseが返されることを確認します。

isNode()は、_this.process.title==="node"_またはnode.js内にある他のグローバルスコープ変数も比較できますが、実際にはグローバルと比較するだけで十分です。

http://jsfiddle.net/p6yedbqk/

[〜#〜] note [〜#〜]:検出実行中の環境は推奨されません。ただし、グローバルスコープの既知の特性を備えた開発およびテスト環境などの特定の環境では役立ちます。

今-答えの2番目の部分。環境検出が行われた後、使用する環境ベースの戦略を選択できます(ある場合) 「グローバル」な変数をアプリケーションにバインドします。

私の意見では、ここで推奨される戦略は、シングルトンパターンを使用して設定をクラス内にバインドすることです。 SOには既に代替の良いリストがあります

JavaScriptでシングルトンを実装する最も簡単でクリーンな方法?

したがって、「グローバル」変数を必要とせず、環境検出をまったく必要としない場合は、シングルトンパターンを使用して値を保存するモジュールを定義するだけであることが判明する場合があります。 OK、モジュール自体はグローバル変数であると主張することができます。JavaScriptでは実際にはそうですが、少なくとも理論的には、それは少しきれいな方法に見えます。

*) https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function

注:Functionコンストラクターで作成された関数は、作成コンテキストのクロージャーを作成しません。それらは常にグローバルスコープで作成されます。それらを実行すると、それらは自分のローカル変数とグローバル変数にのみアクセスでき、Functionコンストラクターが呼び出されたスコープの変数にはアクセスできません。

73
Tero Tolonen

どうやらNode.jsには両方(w/NW.js?)がある可能性があるため、私の個人的な方法はnodeエントリがprocess.versionsオブジェクトに存在するかどうかを検出することです。

var isNode = false;    
if (typeof process === 'object') {
  if (typeof process.versions === 'object') {
    if (typeof process.versions.node !== 'undefined') {
      isNode = true;
    }
  }
}

複数レベルの条件は、一部のブラウザーの制限による未定義変数の検索中のエラーを回避することです。

リファレンス: https://nodejs.org/api/process.html#process_process_versions

14
Dan. B.

状況に応じて、変数ウィンドウまたはグローバルにアタッチできます。マルチプラットフォームのJSアプリケーションを作成する推奨方法ではありませんが:

var app = window ? window : global;

グローバル変数を使用する方がはるかに優れています。グローバル変数は、アプリケーションのロジックで使用されますが、異なるプラットフォームに基づいた部分で構成されます。何かのようなもの:

var app = {
    env: '',
    agent: ''
};

if (window) {
    app.env = 'browser';
    app.agent = navigator.userAgent;
} else if (process) {
    app.env = 'node';
}

そのため、メインアプリケーションのロジックはまったく同じであり、同じオブジェクトを使用します。環境に基づいてグローバルオブジェクトのみを変更する必要があります。これにより、プラットフォームに関してアプリケーションの移植性と柔軟性が大幅に向上します。

7
moka

このためだけのnpmパッケージがあり、クライアント側とサーバー側の両方で使用できます。

ブラウザまたはノード

このように使用できます

if (isBrowser) {
  // do browser only stuff
}

if (isNode) {
  // do node.js only stuff
}

免責事項:私はこのパッケージの著者です:)

3
Dinesh Pandiyan

私はこれが(1。5年)の古い質問に対する遅い答えであることを知っていますが、なぜ jQuery のソースコードをコピーしないのですか?

if (typeof module === "object" && typeof module.exports === "object") {
  // node
}

if (typeof window !== "undefined" && typeof window.document !== "undefined") {
  // browser
}

幸運を。

0
LogicalBranch