Zmienne globalne node.js?

208

Zapytałem tutaj: node.js wymagają dziedziczenia?

i powiedziano mi, że mogę ustawić zmienne w zakresie globalnym, pomijając zmienną.

To mi nie działa.

to znaczy:

_ = require('underscore');

Nie udostępnia _ dla wymaganych plików. Mogę ustawić za pomocą ekspresu app.seti mieć go dostępnego gdzie indziej.

Czy ktoś może potwierdzić, że to ma zadziałać? Dzięki.

Złupić
źródło
Gdzie masz powyższą linię?
Jan Hančič
3
Myślę, że nie powinieneś zaczynać nowego pytania, jeśli odpowiedź na poprzednie pytanie nie działa. Zamiast tego dodaj komentarz i usuń zaakceptowany tag.
alienhard
5
Sama edycja powoduje, że pojawia się na aktualnie aktywnej liście pytań.
MAK
3
Zastosowanie exports. Jest o wiele lepszy.
Emmerman
1
Może to nie działa, ponieważ masz „surowe”; na górze pliku. Dla mnie to działa.
Geza Turi

Odpowiedzi:

237

Możesz użyć globaltak:

global._ = require('underscore')
masylum
źródło
28
Czy możesz podać trochę więcej informacji? Czy to część javascript, czy część węzła? Czy to dobry wzór do naśladowania? Jak powinienem to zrobić, czy powinienem użyć zestawu ekspresowego? Dzięki
Harry
4
Poprzedni komentarz jest niepoprawny. W przeglądarce windowznajduje się obiekt globalny. documentjest własnością window.
G-Wiz
77
To NIE jest dobry wzór do naśladowania. Nie rób tego Konwencja polegająca na używaniu „wymagania” do rozdzielania modułów jest dobrze przemyślana. Nie powinieneś naruszać tego bez dobrego powodu. Zobacz moją odpowiedź poniżej.
Dave Dopson
Ogólnie należy unikać globałów, ale jeśli naprawdę chcesz ich użyć. Wszystkie poniższe 3 instrukcje są równoważne i przypiszą zmienną do zakresu globalnego: GLOBAL._ = wymagana („podkreślenie”); global._ = wymagany („podkreślenie”); _ = wymagany („podkreślenie”);
metaColin,
Kiedy twój projekt zacznie się trochę powiększać, stanie się koszmarem do utrzymania. Proszę spojrzeć na moje podejście.
Oliver Dixon
219

W węźle można ustawić zmienne globalne za pomocą obiektu „global” lub „GLOBAL”:

GLOBAL._ = require('underscore'); // but you "shouldn't" do this! (see note below)

lub bardziej pożytecznie ...

GLOBAL.window = GLOBAL;  // like in the browser

Ze źródła węzła widać, że są one aliasy do siebie:

node-v0.6.6/src/node.js:
28:     global = this;
128:    global.GLOBAL = global;

W powyższym kodzie „to” jest kontekstem globalnym. W systemie modułów commonJS (z którego korzysta węzeł) obiekt „ten” w module (tj. „Twój kod”) NIE jest kontekstem globalnym. Aby to udowodnić, zobacz poniżej, gdzie wypluwam „ten” obiekt, a następnie gigantyczny „GLOBALNY” obiekt.

console.log("\nTHIS:");
console.log(this);
console.log("\nGLOBAL:");
console.log(global);

/* outputs ...

THIS:
{}

GLOBAL:
{ ArrayBuffer: [Function: ArrayBuffer],
  Int8Array: { [Function] BYTES_PER_ELEMENT: 1 },
  Uint8Array: { [Function] BYTES_PER_ELEMENT: 1 },
  Int16Array: { [Function] BYTES_PER_ELEMENT: 2 },
  Uint16Array: { [Function] BYTES_PER_ELEMENT: 2 },
  Int32Array: { [Function] BYTES_PER_ELEMENT: 4 },
  Uint32Array: { [Function] BYTES_PER_ELEMENT: 4 },
  Float32Array: { [Function] BYTES_PER_ELEMENT: 4 },
  Float64Array: { [Function] BYTES_PER_ELEMENT: 8 },
  DataView: [Function: DataView],
  global: [Circular],
  process: 
   { EventEmitter: [Function: EventEmitter],
     title: 'node',
     assert: [Function],
     version: 'v0.6.5',
     _tickCallback: [Function],
     moduleLoadList: 
      [ 'Binding evals',
        'Binding natives',
        'NativeModule events',
        'NativeModule buffer',
        'Binding buffer',
        'NativeModule assert',
        'NativeModule util',
        'NativeModule path',
        'NativeModule module',
        'NativeModule fs',
        'Binding fs',
        'Binding constants',
        'NativeModule stream',
        'NativeModule console',
        'Binding tty_wrap',
        'NativeModule tty',
        'NativeModule net',
        'NativeModule timers',
        'Binding timer_wrap',
        'NativeModule _linklist' ],
     versions: 
      { node: '0.6.5',
        v8: '3.6.6.11',
        ares: '1.7.5-DEV',
        uv: '0.6',
        openssl: '0.9.8n' },
     nextTick: [Function],
     stdout: [Getter],
     arch: 'x64',
     stderr: [Getter],
     platform: 'darwin',
     argv: [ 'node', '/workspace/zd/zgap/darwin-js/index.js' ],
     stdin: [Getter],
     env: 
      { TERM_PROGRAM: 'iTerm.app',
        'COM_GOOGLE_CHROME_FRAMEWORK_SERVICE_PROCESS/USERS/DDOPSON/LIBRARY/APPLICATION_SUPPORT/GOOGLE/CHROME_SOCKET': '/tmp/launch-nNl1vo/ServiceProcessSocket',
        TERM: 'xterm',
        SHELL: '/bin/bash',
        TMPDIR: '/var/folders/2h/2hQmtmXlFT4yVGtr5DBpdl9LAiQ/-Tmp-/',
        Apple_PubSub_Socket_Render: '/tmp/launch-9Ga0PT/Render',
        USER: 'ddopson',
        COMMAND_MODE: 'unix2003',
        SSH_AUTH_SOCK: '/tmp/launch-sD905b/Listeners',
        __CF_USER_TEXT_ENCODING: '0x12D732E7:0:0',
        PATH: '/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:~/bin:/usr/X11/bin',
        PWD: '/workspace/zd/zgap/darwin-js',
        LANG: 'en_US.UTF-8',
        ITERM_PROFILE: 'Default',
        SHLVL: '1',
        COLORFGBG: '7;0',
        HOME: '/Users/ddopson',
        ITERM_SESSION_ID: 'w0t0p0',
        LOGNAME: 'ddopson',
        DISPLAY: '/tmp/launch-l9RQXI/org.x:0',
        OLDPWD: '/workspace/zd/zgap/darwin-js/external',
        _: './index.js' },
     openStdin: [Function],
     exit: [Function],
     pid: 10321,
     features: 
      { debug: false,
        uv: true,
        ipv6: true,
        tls_npn: false,
        tls_sni: true,
        tls: true },
     kill: [Function],
     execPath: '/usr/local/bin/node',
     addListener: [Function],
     _needTickCallback: [Function],
     on: [Function],
     removeListener: [Function],
     reallyExit: [Function],
     chdir: [Function],
     debug: [Function],
     error: [Function],
     cwd: [Function],
     watchFile: [Function],
     umask: [Function],
     getuid: [Function],
     unwatchFile: [Function],
     mixin: [Function],
     setuid: [Function],
     setgid: [Function],
     createChildProcess: [Function],
     getgid: [Function],
     inherits: [Function],
     _kill: [Function],
     _byteLength: [Function],
     mainModule: 
      { id: '.',
        exports: {},
        parent: null,
        filename: '/workspace/zd/zgap/darwin-js/index.js',
        loaded: false,
        exited: false,
        children: [],
        paths: [Object] },
     _debugProcess: [Function],
     dlopen: [Function],
     uptime: [Function],
     memoryUsage: [Function],
     uvCounters: [Function],
     binding: [Function] },
  GLOBAL: [Circular],
  root: [Circular],
  Buffer: 
   { [Function: Buffer]
     poolSize: 8192,
     isBuffer: [Function: isBuffer],
     byteLength: [Function],
     _charsWritten: 8 },
  setTimeout: [Function],
  setInterval: [Function],
  clearTimeout: [Function],
  clearInterval: [Function],
  console: [Getter],
  window: [Circular],
  navigator: {} }
*/

** Uwaga: jeśli chodzi o ustawienie „GLOBAL._”, ogólnie powinieneś po prostu to zrobić var _ = require('underscore');. Tak, robisz to w każdym pliku używającym podkreślenia, tak jak robisz to w Javie import com.foo.bar;. Ułatwia to określenie działania kodu, ponieważ powiązania między plikami są „jawne”. Lekko irytujące, ale dobre. .... To jest głoszenie.

Istnieje wyjątek od każdej reguły. Miałem dokładnie JEDEN przykład, w którym musiałem ustawić „GLOBAL._”. Tworzyłem system do definiowania plików „config”, które były w zasadzie JSON, ale zostały „napisane w JS”, ​​aby umożliwić nieco większą elastyczność. Takie pliki konfiguracyjne nie miały instrukcji „wymagają”, ale chciałem, aby miały one dostęp do podkreślenia (system ENTIRE był oparty na szablonach podkreślenia i podkreślenia), dlatego przed oceną „config” ustawiłbym „GLOBAL._”. Więc tak, dla każdej reguły jest gdzieś wyjątek. Ale powinieneś mieć naprawdę dobry powód i nie tylko „mam dość pisania„ wymagaj ”, więc chcę zerwać z konwencją”.

Dave Dopson
źródło
7
Jakie są wady korzystania z GLOBAL? Dlaczego potrzebuję naprawdę dobrego powodu? Najważniejsze jest to, że moja aplikacja działa, prawda?
trusktr
26
ostatecznie tak, jeśli wysyłasz, to wszystko się liczy. Jednak niektóre praktyki są znane jako „najlepsze praktyki”, a ich przestrzeganie zwykle zwiększa szanse wysyłki i / lub możliwości zachowania tego, co zbudowałeś. Znaczenie przestrzegania „dobrych praktyk” wzrasta wraz z rozmiarem projektu i jego długowiecznością. Wbudowałem wszelkiego rodzaju paskudne hacki w krótkotrwałe projekty, które były napisane raz, nigdy nie czytaj (i „jeden programista”). W większym projekcie tego rodzaju cięcie narożników kosztuje pęd projektowy.
Dave Dopson
48
W szczególności w przypadku GLOBAL problem dotyczy czytelności. Jeśli Twój program korzysta z globalnych zmiennych, oznacza to, że aby zrozumieć kod, muszę zrozumieć dynamiczny stan uruchomieniowy całej aplikacji. Dlatego programiści są nieufni wobec globali. Jestem pewien, że istnieją dziesiątki sposobów skutecznego ich wykorzystania, ale w większości widzieliśmy, że są wykorzystywani przez młodszych programistów do wad produktu.
Dave Dopson,
2
Dlaczego nie możesz po prostu umieścić swoich konfiguracji w zwykłym .jspliku i zadzwonić requireprzed wyeksportowaniem konfiguracji?
Azat,
4
@Jackie - en.wikipedia.org/wiki/Singleton_pattern . jeśli robisz mapy ze wzorem Singleton, może to mieć sens. Połączenia DB mogą być singletonami, gdy: 1) konfiguracja jest droga, 2) chcesz, aby połączenie było konfigurowane tylko raz, 3) obiekt połączenia jest długowieczny i nie wejdzie w stan awarii w przypadku czkawek sieciowych, 4) obiekt połączenia jest bezpieczny dla wątków / może być współdzielony przez wiele różnych rozmówców.
Dave Dopson,
78

Inne rozwiązania wykorzystujące słowo kluczowe GLOBAL to koszmar w utrzymaniu / czytelności (+ zanieczyszczenie przestrzeni nazw i błędy), gdy projekt staje się większy. Widziałem ten błąd wiele razy i miałem problem z jego naprawieniem.

Użyj pliku JS, a następnie użyj eksportu modułów.

Przykład:

globals.js

var Globals = {
    'domain':'www.MrGlobal.com';
}

module.exports = Globals;

Następnie, jeśli chcesz ich użyć, użyj wymagają.

var globals = require('globals'); //<< globals.js path
globals.domain //<< Domain.
Oliver Dixon
źródło
12
Na pewno nie kocham jednorożców, ale lubię twoje podejście. Dzięki.
Jonatas Walker
A co ze zmianą globals.domain?
Fizzix
1
@iLoveUnicorns dzięki za odpowiedź. Zajmę się alternatywami, takimi jak „sesja ekspresowa”, ponieważ potrzebuję go głównie do przechowywania zalogowanych danych użytkownika.
Fizzix
11
Chociaż moim zdaniem jest to lepsze podejście, nie tworzy globałów i nie odpowiada na zadane pytanie. Jest to podejście alternatywne i zawsze zachęcam do tego, jednak zwykła uparta uparta wypowiedź typu „To jedyna poprawna odpowiedź w tym wątku” po prostu nie pasuje tutaj. stackoverflow.com/help/be-nice
Thor84no
2
To może być lepsze podejście, ale jeśli próbujesz uruchamiać skrypty autorstwa zewnętrznego, które polegają na tym, że coś znajduje się w globalnej przestrzeni nazw, to ci nie pomaga. IOW, to nie odpowiada na pytanie.
binki,
12

A co z globalną przestrzenią nazw global.MYAPI = {}

global.MYAPI._ = require('underscore')

Edytuj po komentarzu camilo-martin : Wszyscy inni plakaty mówią o związanym z tym złym wzorcu. Pomijając tę ​​dyskusję, najlepszym sposobem globalnego zdefiniowania zmiennej (pytanie OP) jest użycie przestrzeni nazw.

@tip: http://thanpol.as/javascript/development-using-namespaces

Igor Parra
źródło
3
Po to requirejest! Można używać przestrzeni nazw, ale nie wszystkie global.foo = global.foo || {}na wszystkich plikach czy coś w tym stylu. Wymagaj pliku, który definiuje przestrzeń nazw. Zrób to dla dzieci.
Camilo Martin
@ camilo-martin Cześć, 1) Poprzez zdefiniowanie global.MYAPI._ nie musisz definiować go we wszystkich plikach, to jest powód bycia globalnym. 2) To nie musi być z dziećmi. Nawet jeśli wszyscy mówią, że jest to zły wzór, to zależy od programisty i konkretnej sytuacji, w jaki sposób korzysta on z tej możliwości języka.
Igor Parra
2
Tak, ale powiedzmy, że deklarujesz niektóre funkcje przestrzeni nazw w osobnym pliku. Następnie potrzebujesz pliku, aby użyć obiektu, co jest odwrotne i jest sprzeczne z CommonJS i CommonSense. Jeśli będziesz potrzebować różnych rzeczy, kod użytkownika wymaga przestrzeni nazw i nie będzie wymagany przez przestrzeń nazw. Uwaga: Nie mówię nic przeciwko przestrzeniom nazw, po prostu istnieją konwencje dotyczące tego, kto dzwoni do kogo z jakiegoś powodu. A po stronie klienta nie masz tego, co ma węzeł; zobacz link, o którym wspominasz, robi rzeczy w określony sposób (poprzez globalny), ponieważ dotyczy przeglądarki, a nie węzła.
Camilo Martin
1
Niestety opublikowany adres URL działa tylko wtedy, gdy pominiesz końcowy ukośnik;)
Diriable
10

Możesz po prostu użyć obiektu globalnego.

var X = ['a', 'b', 'c'];
global.x = X;

console.log(x);
//['a', 'b', 'c']
Joao Falcao
źródło
5

Zgadzam się, że używanie globalnej / GLOBALNEJ przestrzeni nazw do ustawiania czegokolwiek globalnego jest złą praktyką i nie używam jej wcale w teorii ( teoretycznie jest to słowo operacyjne). Jednak (tak, operacyjny) używam go do ustawiania niestandardowych klas błędów:

// Some global/config file that gets called in initialisation

global.MyError = [Function of MyError];

Tak, tabu tutaj, ale jeśli twoja strona / projekt używa niestandardowych błędów w całym miejscu, w zasadzie musisz go zdefiniować wszędzie lub przynajmniej gdzieś, aby:

  1. Zdefiniuj przede wszystkim klasę Error
  2. W skrypcie, w który go rzucasz
  3. W skrypcie, w którym go łapiesz

Definiowanie własnych niestandardowych błędów w globalnej przestrzeni nazw oszczędza mi kłopotów z wymaganiem mojej biblioteki błędów klienta. Obrazowanie zgłaszające błąd niestandardowy, w przypadku gdy błąd niestandardowy jest niezdefiniowany.

Również jeśli jest to źle, proszę dać mi znać, ponieważ dopiero niedawno zacząłem to robić

DrunkenBeetle
źródło