Определение независимой от реализации версии глобального объекта в JavaScript - PullRequest
6 голосов
/ 26 ноября 2011

Я пытаюсь определить объект global в JavaScript в одну строку следующим образом:

var global = this.global || this;

Вышеупомянутое утверждение находится в глобальной области видимости. Следовательно, в браузерах указатель this является псевдонимом для объекта window. Предполагая, что это первая строка JavaScript, которая будет выполнена в контексте текущей веб-страницы, значение global всегда будет таким же, как значение указателя this или объекта window.

В реализациях CommonJS, таких как RingoJS и node.js, указатель this указывает на текущий ModuleScope. Однако мы можем получить доступ к объекту global через свойство global, определенное в ModuleScope. Следовательно, мы можем получить к нему доступ через свойство this.global.

Следовательно, этот фрагмент кода работает во всех браузерах и, по крайней мере, в RingoJS и node.js, но я не тестировал другие реализации CommomJS. Поэтому я хотел бы знать, не даст ли этот код правильных результатов при запуске в любой другой реализации CommonJS, и если да, то как я могу это исправить.

В конце концов, я намереваюсь использовать его в лямбда-выражении для моей независимой от реализации структуры JavaScript следующим образом (идея из jQuery):

(function (global) {
    // javascript framework
})(this.global || this);

Ответы [ 3 ]

5 голосов
/ 26 ноября 2011

this никоим образом не относится к области применения.

(function(){
    (function(){
        (function(){

            (function(){
            alert( this ); //global object
            })()

        }).bind({})()
    }).apply({})
}).call({})

this разрешается только во время вызова функции и сводится к нескольким простым правилам.

  1. Если функция вызывается как свойство некоторого объекта, тогда этот объект будет this внутри функции
  2. Если функция вызывается как есть, this будет неопределенным, поэтому в нестрогом режиме это будет глобальный объект
  3. Если функция вызывается с помощью .call/.apply, тогда this устанавливается непосредственно вами.

Таким образом, как вы можете видеть, оно подпадает под правило № 2, которое разрешается до undefined. И поскольку нет "use strict";:

установить ThisBinding для глобального объекта

Редактировать: Теперь я провел несколько быстрых тестов в RingoJS, и они фактически поместили «глобальный объект» внутри реального глобального объекта (как определено стандартами), который равен ModuleScope. Просто потому, что фактический глобальный объект в большинстве реализаций js имеет Object и String и т. Д., Не делает объект глобальным, если он также содержит эти объекты. Причина, по которой вы можете получить доступ к String и Object в RingoJS, заключается в том, что они помещают их в прототип ModuleScope:

var logs = require('ringo/logging').getLogger("h");

logs.info( Object.getPrototypeOf( this ) === this.global );
//true

Еще одно доказательство того, что ModuleScope является действительным глобальным объектом:

this.property = "value";
logs.info( property );
//"value"

Так что от такого рода хитрости ничего не получится, это ничего не исправит:

function injectGlobal(){
globalProperty = "value"; // "use strict" would fix this!
}

injectGlobal()

logs.info( globalProperty );
//"value"

Rant over, this относится к реальному глобальному объекту уже в соответствии с правилами, приведенными ранее в этом посте. this.global это не реальный глобальный объект, как определено стандартами, это просто контейнер.

Кроме того, вы можете эмулировать это поведение в браузерах:

Рассмотрим scopehack.js

this.global = window.global || top.global || {};

Рассмотрим main.html:

<script src="scopehack.js"></script>
<script>
this.global.helloWorld = "helloWorld"; //"global scope"
this.helloWorld = "helloWorld" //"ModuleScope"
</script>

<iframe src="module.html"></iframe>

И, наконец, "module" module.html:

<script src="scopehack.js"></script>
<script>
    with( this.global ) { //poor mans RhinoJS scope injection, doesn't work for writing
        console.log( helloWorld ); //"global scope" - "helloWorld"
        console.log( this.helloWorld ); //"ModuleScope" undefined
    }
</script>

Какой из них является глобальным объектом как в module.html, так и в main.html? Это все еще this.

TLDR:

var obj = {
"String": String,
"Object": Object,
.....
};

Не делает obj глобальным объектом.

2 голосов
/ 29 ноября 2011

Независимая от реализации версия не является тривиальной

(function (global) {
    // javascript framework
})(
   this && this.global || // ringoJS
   typeof root !== "undefined" && root || // node.js
   typeof global !== "undefined" && global || // more node.js
   typeof GLOBAL !== "undefined" && GLOBAL || // more node.js
   typeof window !== "undefined" && window || // browsers
   this // either undefined or some global default?
);

Вам потребуется жесткий код при обнаружении функций для каждой среды.

1 голос
/ 02 декабря 2011

Прочитав ответы Эсайлии и Райноса, я понял, что мой код this.global || this не будет работать для всех случаев в node.js; и что он может даже потерпеть неудачу в браузерах, если переменная с именем global уже существует в глобальной области видимости.

Эсайлия указала, что this.global на самом деле не является global объектом, заявив, что this - это global объект в RingoJS; и хотя я понимаю его аргументы, для моих целей мне требуется this.global, а не this.

Рейнос предложил мне жестко определять функции кода для каждой среды CommonJS. Однако, поскольку в настоящее время я поддерживаю только RingoJS и node.js, мне нужно проверить только на global и window. Поэтому я решил придерживаться this.global || this.

Тем не менее, как я уже говорил, this.global || this не работает для всех случаев в node.js, как я понял из комментариев Бенви. В REPL node.js я понял, что мне нужно this, а не this.global. Однако this.global || this выражает this.global. В модуле node.js мне требуется this.global, а не this. Однако оно выражает this, поскольку this.global равно undefined. Следовательно, чтобы решить эту проблему, я решил использовать следующий код:

(function (global) {
    // javascript framework
})(typeof global !== "undefined" && global || this);

Причина, по которой я использую этот код, заключается в том, что в модулях node.js this.global равен undefined. Следовательно, мы должны использовать global напрямую. Таким образом, мы используем typeof global !== "undefined" && global, чтобы получить объект global как в RingoJS, так и в node.js; и мы используем this как global объект в браузерах (window) и как запасной вариант по умолчанию.

Примечание: Я не предоставил никакой логики для поиска объекта global в REPL node.js, потому что я не верю, что мой фреймворк в любом случае будет использоваться непосредственно в REPL. Тем не менее, написание логики для ее поиска должно быть довольно тривиальным, как только вы поймете сложности поиска global объекта в node.js, как указал Бенви. Я знаю, что не знаю.

...