Як отримати глобальний об'єкт у JavaScript?


77

Я хочу перевірити сценарій, чи певний інший модуль вже завантажений.

if (ModuleName) {
    // extend this module
}

Але якщо ModuleNameне існує, це throws.

Якби я знав, що це таке, Global Objectя міг би цим скористатися.

if (window.ModuleName) {
    // extend this module
}

Але так як я хочу , щоб мій модуль для роботи з обома браузерами і node, rhinoі т.д., я не можу припустити window.

Як я розумію, це не працює в ES 5 з "use strict";

var MyGLOBAL = (function () {return this;}()); // MyGlobal becomes null

Це також не вдасться, за винятком виключення

var MyGLOBAL = window || GLOBAL

Тож, здається, я залишився

try {
    // Extend ModuleName
} 
catch(ignore) {
}

Жоден із цих випадків не пройде JSLint.

Мені чогось не вистачає?


Зверніть увагу, що "var Fn = Функція, global = Fn ('повернути це') ();" буде НЕ проходити JSLint, так як JSLint очікує функції з великої літери , щоб бути конструктором і називатися з «новим». Однак це просте виправлення.
користувач4815162342

Це також проходить JSLint, і для цього не потрібна додаткова Fnзмінна:var global = (function (fn) { return fn('return this'); }(Function));
ahuth

@ahuth Вам потрібна додаткова сума ().
wizzwizz4

Ви можете отримати глобальний об'єкт , як це (без Eval або функцію конструктора): var global = (function(){return this}).apply(null). Більше інформації на developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
mems

1
Примітка: apply (null) не дає глобального об'єкта, якщо ви використовуєте суворий режим: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
меми

Відповіді:


95

Ну, ви можете використовувати typeofоператор, і якщо ідентифікатор не існує в жодному місці ланцюжка області дії, він не видасть a ReferenceError, а просто поверне "undefined":

if (typeof ModuleName != 'undefined') {
  //...
}

Пам'ятайте також, що thisзначення глобального коду відноситься до глобального об'єкта, тобто, якщо ваше ifтвердження знаходиться в глобальному контексті, ви можете просто перевірити this.ModuleName.

Щодо (function () { return this; }());техніки, ви маєте рацію, в суворому режимі thisзначення просто буде undefined.

У суворому режимі є два способи отримати посилання на глобальний об’єкт, незалежно від того, де ви знаходитесь:

  • Через Functionконструктор:

    var global = Function('return this')();
    

Функції, створені за допомогою Functionконструктора, не успадковують строгість того, хто викликає, вони строгі, лише якщо вони запускають своє тіло з 'use strict'директивою, інакше вони не є суворими.

Цей метод сумісний з будь-якою реалізацією ES3.

  • Через непрямий evalдзвінок , наприклад:

    "use strict";
    var get = eval;
    var global = get("this");
    

Вищезазначене буде працювати, оскільки в ES5, непрямі виклики eval, використовують глобальне середовище як одночасно, змінне середовище та лексичне середовище для коду eval.

Детальніше про введення коду Eval , крок 1.

Але майте на увазі, що останнє рішення не буде працювати на реалізаціях ES3, оскільки непрямий виклик evalна ES3 використовуватиме змінні та лексичні середовища абонента як середовища для самого eval-коду.

І нарешті, ви можете виявити корисним виявити, чи підтримується суворий режим:

var isStrictSupported = (function () { "use strict"; return !this; })();

5
+1 @CMS - Я втратив підрахунок, скільки разів читав ваші відповіді на цьому сайті. Спасибі людино.
screenm0nkey

1
Можливо очевидне запитання: навіщо використовувати "використовувати строго", а потім обійти один із його наслідків? Чи є причина, що єдиний спосіб отримати глобальний контекст - це трохи хакі?
mowwwalker

1
@Walkerneo однією з причин може бути те, що ми дійсно не повинні додавати що-небудь до глобальної області (хоча це може бути корисним, наприклад, коли нам потрібно експортувати API бібліотеки). Крім того, я вважаю, що використання "цього" обмежено (у суворому режимі), оскільки не завжди може бути очевидно, до чого це відноситься. Досягнення цього ускладнює отримання посилання на глобальний об’єкт.
ahuth

4
На жаль, функція ('повернути це') не працює. У Chrome я отримую: EvalError: відмовлено оцінювати рядок як JavaScript, оскільки 'unsafe-eval' не є дозволеним джерелом сценарію в наступній директиві Політики безпеки вмісту: "script-src 'self' 'unsafe-inline'".
Пт

1
Зауваження @CMS, яке Functionможе бути замінено або навіть затінено в дочірній області. Краще використовуватиvar global = (() => {}).constructor('return this')();

24

Оновлення 2019

З усіма сучасними Webpacks і Broccolis, і Gulps and Grunts, і TypeScripts і AltScripts, а також create-response-apps тощо, це досить марно, але якщо ви просто працюєте з простими, старими, VanillaJS і хочете зробити це ізоморфно, це, мабуть, ваш найкращий варіант:

var global
try {
  global = Function('return this')();
} catch(e) {
  global = window;
}

Виклик конструктора функції працюватиме навіть при використанні --use_strictу вузлі, оскільки конструктор функції завжди виконується в глобальній несуворій області.

Якщо конструктор функцій виходить з ладу, це тому, що ви знаходитесь у браузері, де evalвимкнено заголовки CSP.

Звичайно, з Діно на шляху (заміна вузлів), вони можуть також заборонити конструктор Function, в цьому випадку він повернувся до перерахування об'єктів , таких як global, module, exports, globalThisі window, а потім перевірка качка типу , яка є глобальним вичерпно ... : - /

Божевільне однорядкове рішення (оригінал):

var global = Function('return this')() || (42, eval)('this');

.

.

.

Працює

  • в кожному середовищі (що я тестував)
  • в суворому режимі
  • і навіть у вкладеному масштабі

Оновлення 2014-вересня-23

Тепер це може не вдатися, якщо заголовки HTTP в останніх браузерах явно забороняють eval.

Обхідним шляхом було б спробувати / зловити оригінальне рішення, оскільки лише браузери, як відомо, запускають цей тип підмножини JavaScript.

var global;

try {
  global = Function('return this')() || (42, eval)('this');
} catch(e) {
  global = window;
}
Example:
---

    (function () {

      var global = Function('return this')() || (42, eval)('this');
      console.log(global);

      // es3 context is `global`, es5 is `null`
      (function () {
        "use strict";

        var global = Function('return this')() || (42, eval)('this');
        console.log(global);

      }());

      // es3 and es5 context is 'someNewContext'
      (function () {

        var global = Function('return this')() || (42, eval)('this');
        console.log(global);

      }).call('someNewContext');

    }());

Tested:
---

  * Chrome v12
  * Node.JS v0.4.9
  * Firefox v5
  * MSIE 8

Why:
---

In short: it's some weird quirk. See the comments below (or the post above)


In `strict mode` `this` is never the global, but also in `strict mode` `eval` operates in a separate context in which `this` *is* always the global.

In non-strict mode `this` is the current context. If there is no current context, it assumes the global. An anonymous function has no context and hence in non-strict mode assumes the global.

Sub Rant:

There's a silly misfeature of JavaScript that 99.9% of the time just confuses people called the 'comma operator'.

    var a = 0, b = 1;
    a = 0, 1;          // 1
    (a = 0), 1;        // 1
    a = (0, 1);        // 1
    a = (42, eval);    // eval
    a('this');         // the global object

1
Це не просто дивна химерність, це те, що я описав у своїй відповіді як непрямий закликeval . У ES5 виклик до evalє прямим лише у тому випадку, якщо CallExpressionвін сформований а, MemberExpressionщо відповідає двом умовам: 1. Базовим значенням посилання є запис середовища. 2. Ім'я посилання полягає в тому "eval", що будь-який інший спосіб виклику evalпризведе до непрямого дзвінка. Будьте обережні, оскільки така поведінка не буде працювати в ES3, оскільки концепція прямих викликів до eval не існувала. Спробуйте цей приклад із реалізацією ES3 (наприклад, IE8)
Крістіан Сальвадо,

3
@ CoolAJ86, ваш новий код буде працювати і на реалізаціях ES3, але якщо ви уважно його вивчите, то помітите, що непряма evalчастина взагалі не потрібна, все, що вам потрібно, це var global = Function('return this')();, як я вже описав, "Функції, створені за допомогою Functionконструктора don не успадковує суворості абонента ", що означає, що повернене значення цієї функції завжди буде глобальним об'єктом, незалежно від реалізації-. evalВиклик на правій стороні ||оператора, ніколи не буде, так як функція завжди буде давати значення truthy (глобальний об'єкт).
Крістіан С. Сальвадо,

1
Можливо, ви додаєте до плутанини з комами, вказуючи a = 0, 1; // 0і (a = 0), 1; // 0як обидва вирази повертаються 1. Можливо, // a = 0було б і краще.
MikeM

Хм ... я міг присягнути, коли тестував, що отримав 0 ... але ви точно маєте рацію. оновив anwser
coolaj86

Коли буде (42, eval)('this')оцінено вираз ? Іншими словами, коли буде Function('return this')()помилковим?
zVictor

6

Чому б просто не використовувати це в глобальному масштабі як параметр для функції обгортки, як показано нижче?

(function (global) {
    'use strict';
    // Code
}(this));

2
Дивно, що ще ніхто не вказував, що це насправді не додає нічого, що відповідь Саболча Курді ще не містився роком раніше. Він не стосується питання, порушеного в коментарях там, тобто те, що його потрібно викликати в глобальному масштабі, щоб працювати, але принаймні ваша відповідь це визнає.

Це не працює в модулі NodeJS, де this === module.exports.
лапо

4

Ось вам :)

var globalObject = (function(){return this;})();

Це має працювати з будь-якого місця, наприклад, з іншого закриття.

Редагувати - просто уважніше прочитайте свою публікацію і побачите частину про строгий режим ES5. Хтось може пролити на це трохи більше світла? Це був прийнятий спосіб отримати глобальний об’єкт, наскільки я пам’ятаю ... Я сподіваюся, він не закінчиться зламаним.

Редагування 2 - Відповідь CMS містить більше інформації про обробку суворого режиму ES5 this.


@Eduardo ―Нічого.
Константин Ван

3

Я думаю, що це майже нормально в rhino, node, браузері та з jslint (без додаткових прапорів обходу) - чи допоможе це? Мені чогось не вистачає?

x = 1;
(function(global){
    "use strict";
    console.log(global.x);
}(this));

Хоча я сам, як правило, використовую віконний об'єкт, і якщо мені потрібне безголове тестування, я можу використовувати env.js (rhino) або Phantom (вузол).


Він передає jslint без додаткових опцій (якщо зняти приклад x = 1), хоча це, напевне, альтернатива (хоча елегантність - це дуже суб'єктивний фактор).

2
Вам не вистачає того факту, який thisможе не стосуватися глобального об’єкта.
MikeM

Наскільки я знаю, це стосується глобальної області, якщо використовується в глобальній області (браузер, носоріг, вузол), але я можу помилятися. Чи можете ви показати приклад vm, де він працює по-іншому? Дякую!

1
@SzabolcsKurdi Ви маєте рацію, що `` це '' буде глобальною сферою, коли використовується в глобальній області. Проблема полягає в тому, що немає жодної гарантії того, що функція буде виконуватися в глобальному масштабі. Наприклад, якщо цей get поміщений у бібліотеку та обгорнутий у функцію, яка негайно викликається. Те, що ми справді шукаємо, - це спосіб отримати глобальний обсяг, незалежно від обсягу, в який він покликаний.
ahuth

3

ECMAScript незабаром додасть це до свого стандарту: https://github.com/tc39/proposal-global

Поки цього не зроблено, рекомендується ось що:

var getGlobal = function () {
    // the only reliable means to get the global object is
    // `Function('return this')()`
    // However, this causes CSP violations in Chrome apps.
    if (typeof self !== 'undefined') { return self; }
    if (typeof window !== 'undefined') { return window; }
    if (typeof global !== 'undefined') { return global; }
    throw new Error('unable to locate global object');
};

Це проблематично; це впливає на всі версії до 2.9, і Moment є дуже популярною бібліотекою.
Кну

1

Це не передає jslint: var Fn = Function, global = Fn('return this')();

Спробуйте самі: http://www.jslint.com/

це буде: var Fn = Function, global = new Fn('return this')();

Але насправді це те саме згідно MDN :

Виклик конструктора функції як функції (без використання нового оператора) має той самий ефект, що і виклик його як конструктора.


Цікаве спостереження.
L0j1k

1

Це наступне рішення працює в:

  • Chrome
  • Вузол. JS
  • Firefox
  • MSIE
  • Веб-працівники

Код:

(function (__global) {
  // __global here points to the global object
})(typeof window !== "undefined" ? window : 
   typeof WorkerGlobalScope !== "undefined" ? self :
   typeof global !== "undefined" ? global :
   Function("return this;")());

Вам просто потрібно змінити X на ім'я змінної, яку ви хотіли б


0

У мене була ця проблема раніше, я не задоволений рішенням, але воно працює і передає JSLint (припустимо браузер | припустити вузол):

"use strict";
var GLOBAL;
try{
    /*BROWSER*/
    GLOBAL = window;
}catch(e){
    /*NODE*/
    GLOBAL = global;
}
if(GLOBAL.GLOBAL !== GLOBAL){
    throw new Error("library cannot find the global object");
}

як тільки ви отримаєте GLOBAL var, ви зможете здійснити перевірку та в кінці типу сценарію

delete GLOBAL.GLOBAL;

Я підтверджую, що такий підхід є чистим та корисним. Не впевнений, чому його не голосують більше порівняно з усіма злими рішеннями в інших відповідях.

0

Ось що я використовую:

"use strict";
if(this && this.hasOwnProperty && !this.hasOwnProperty('globalScope')){
    try {
        globalScope = Function('return this')();
    }catch(ex){
        if(this.hasOwnProperty('window')){
            globalScope = window;
        }else{
            throw 'globalScope not found';
        }
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.