Detekcja środowiska: węzeł.js czy przeglądarka

Rozwijam aplikację JS, która musi działać zarówno po stronie klienta ,jak i serwera (w Javascript na przeglądarce i w węźle.js), i chciałbym móc ponownie wykorzystać części kodu, które są używane dla obu stron.

Dowiedziałem się, że {[2] } jest zmienną dostępną tylko w przeglądarkach i global w węźle, więc mogę wykryć w jakim środowisku kod jest wykonywany (zakładając, że żaden skrypt nie deklaruje zmiennej window)

Są dwa problemy.

  1. Jak mam wykryć, w której przeglądarce jest uruchomiony kod. Na przykład, czy ten kod jest OK. (Ten kod jest inline, co oznacza, że jest otoczony jakimś globalnym kodem, używanym ponownie dla obu środowisk)

    if window?
        totalPath= "../examples/#{path}"
    else
        totalPath= "../../examples/#{path}"
    
  2. Jak mogę używać zmiennych globalnych dla obu środowisk ? Robię co następuje, ale to naprawdę nie jest w porządku.

    if window?
        window.DocUtils = {}
        window.docX = []
        window.docXData= []
    else
        global.DocUtils= {}
        global.docX = []
        global.docXData = []
    
Author: edi9999, 2013-07-10

4 answers

uwaga: to pytanie miało dwie części, ale ponieważ tytuł brzmiał " Environment detection: node.js czy browser " - najpierw przejdę do tej części, bo chyba Wiele osób przyjeżdża tu szukać odpowiedzi na to pytanie. Osobne pytanie może być w porządku.

W JavaScript zmienne mogą być redefiniowane przez wewnętrzne zakresy, więc zakładając, że środowisko nie utworzyło zmiennych nazwanych jako proces, global lub window może łatwo zawieść, na przykład jeśli używa się węzła.js moduł jsdom, przykład użycia API ma

var window = doc.defaultView;

Po czym wykrycie środowiska na podstawie istnienia zmiennej window systematycznie zawiedzie dowolny moduł działający w tym zakresie. Z tą samą logiką każdy kod oparty na przeglądarce może łatwo zastąpić global lub process, ponieważ nie są to zastrzeżone zmienne w tym środowisku.

Na szczęście istnieje sposób na Wymaganie globalnego zakresu i przetestowanie, co to jest - jeśli utworzysz nową funkcję za pomocą new Function() konstruktor, zakres wykonania this jest powiązany z zakresem globalnym i możesz porównać zakres globalny bezpośrednio z wartością oczekiwaną. *)

Więc aby utworzyć funkcję Sprawdź, czy globalny zakres jest "okno" byłoby

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");

I funkcja sprawdzająca, czy globalny sope jest powiązany z" globalnym " będzie

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");

Próba... catch-part upewni się, że jeśli zmienna nie jest zdefiniowana, to zostanie zwrócona false.

isNode()może również porównać this.process.title==="node" lub jakiś inny globalny zmienna zakresu znaleziona wewnątrz węzła.js jeśli chcesz, ale w porównaniu do globalnego powinno wystarczyć w praktyce.

Http://jsfiddle.net/p6yedbqk/

Uwaga: wykrywanie uruchomionego środowiska nie jest zalecane. Jednak może być przydatny w specyficznym środowisku, takim jak środowisko programistyczne i testowe, które ma pewne znane cechy dotyczące globalnego zasięgu.

Teraz-druga część odpowiedzi. Po środowisku wykrywanie zostało wykonane, można wybrać, które środowisko oparte strategii chcesz użyć (jeśli w ogóle), aby powiązać swoje zmienne, które są "globalne" do aplikacji.

Zalecaną strategią tutaj, moim zdaniem, byłoby użycie wzorca Singletona do wiązania ustawień wewnątrz klasy. Istnieje dobra lista alternatyw już w So

Najprostszy/najczystszy sposób implementacji Singletona w JavaScript?

Więc może się okazać, jeśli nie potrzebujesz " globalnego" zmienna, i nie potrzebujesz detekcji środowiska w ogóle, wystarczy użyć wzorca singleton do zdefiniowania modułu, który będzie przechowywać wartości dla Ciebie. Ok, można argumentować, że sam moduł jest zmienną globalną, która w JavaScript faktycznie jest, ale przynajmniej w teorii wygląda to trochę czystsze sposób.

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

Uwaga: funkcje utworzone za pomocą funkcji konstruktor nie tworzy zamknięcia ich kontekstu tworzenia; zawsze są tworzone w zasięg globalny. Podczas ich uruchamiania będą mogli uzyskać dostęp tylko własne zmienne lokalne i globalne, a nie te z zakresu w którym został wywołany konstruktor funkcji.

 56
Author: Tero Tolonen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:30:59

Od początku.js może mieć oba (w / NW.js?), moim osobistym sposobem na to jest wykrycie, czy wpis node istnieje w obiekcie process.versions.

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

Multilevel warunków ma na celu unikanie błędów podczas wyszukiwania w niezdefiniowanej zmiennej ze względu na ograniczenia niektórych przeglądarek.

Odniesienie: https://nodejs.org/api/process.html#process_process_versions

 10
Author: Dan. B.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-21 15:27:26

Możesz dołączyć do zmiennej window lub global-w zależności od sytuacji. Chociaż nie jest to zalecany sposób tworzenia wieloplatformowej aplikacji JS:

var app = window ? window : global;

O wiele lepiej jest mieć zmienną globalną, która będzie używana nad logiką aplikacji, ale będzie złożona z części opartych na różnych platformach. Coś w stylu:

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

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

Więc chodzi o to, że główna logika aplikacji będzie absolutnie taka sama i będzie używać tego samego obiektu, tylko że globalny obiekt musi zostać zmieniony w oparciu o środowisko. Dzięki temu aplikacja jest bardziej przenośna i elastyczna pod względem platform.

 8
Author: moka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-03 22:28:54

Istnieje pakiet npm tylko do tego i może być używany zarówno po stronie klienta, jak i po stronie serwera.

Browser-or-node

Możesz go użyć w ten sposób

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

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

Zastrzeżenie: jestem autorem tej paczki:)

 2
Author: Dinesh Pandiyan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-31 07:37:46