Как проверить, работает ли скрипт под node.js? - PullRequest
141 голосов
/ 19 ноября 2010

У меня есть скрипт, который мне нужен из скрипта node.js, который я хочу сохранить независимым от движка javascript.

Итак, например, я хочу сделать:

exports.x = y;

только если он работает под node.js.Как я могу выполнить этот тест?

Редактировать: При публикации этого вопроса я не знал, что функция модулей node.js основана на commonjs .

Для конкретного примера, который я дал, более точный вопрос был бы:

Как скрипт может определить, требуется ли он как модуль commonjs?

Ответы [ 18 ]

89 голосов
/ 19 ноября 2010

Ну, нет надежного способа обнаружения работы в Node.js, поскольку каждый веб-сайт может легко объявить одни и те же переменные, но, поскольку в Node.js по умолчанию нет объекта window, вы можете пойти другим путем и проверить,вы работаете в браузере.

Это то, что я использую для библиотек, которые должны работать как в браузере, так и под Node.js:

if (typeof window === 'undefined') {
    exports.foo = {};

} else {
    window.foo = {};
}

Это может все еще взорваться в случае, еслиwindow определено в Node.js, но нет веской причины, по которой кто-то может это сделать, поскольку вам явно нужно было бы опустить var или установить свойство для объекта global.

РЕДАКТИРОВАТЬ

Чтобы определить, требуется ли ваш скрипт в качестве модуля CommonJS, это опять не просто.CommonJS указывает только то, что A: модули будут включены через вызов функции require, а B: модули экспортируют вещи через свойства объекта exports.Теперь, как это реализовать, оставлено на усмотрение базовой системы.Node.js оборачивает содержимое модуля в анонимную функцию:

function (exports, require, module, __filename, __dirname) { 

См .: https://github.com/ry/node/blob/master/src/node.js#L325

Но не пытаются обнаружить это с помощью какой-то сумасшедшей arguments.callee.toString() вещи, вместо этого просто используйте мой пример кода выше, который проверяет браузер.Node.js намного более чистая среда, поэтому маловероятно, что там будет объявлено window.

76 голосов
/ 04 марта 2011

При поиске поддержки CommonJS , вот как библиотека Underscore.js делает это:

Редактировать: в ваш обновленный вопрос:

(function () {

    // Establish the root object, `window` in the browser, or `global` on the server.
    var root = this; 

    // Create a reference to this
    var _ = new Object();

    var isNode = false;

    // Export the Underscore object for **CommonJS**, with backwards-compatibility
    // for the old `require()` API. If we're not in CommonJS, add `_` to the
    // global object.
    if (typeof module !== 'undefined' && module.exports) {
            module.exports = _;
            root._ = _;
            isNode = true;
    } else {
            root._ = _;
    }
})();

Пример здесь сохраняет шаблон модуля.

30 голосов
/ 05 марта 2016

В настоящее время я наткнулся на неправильное обнаружение узла, которое не не знает об окружении узла в Electron из-за вводящего в заблуждение определения функции.Следующие решения явно определяют среду процесса.


Определить только Node.js

(typeof process !== 'undefined') && (process.release.name === 'node')

Это обнаружит, если вы работаете в Node-процессе, так как process.release содержит «метаданные, относящиеся к текущей версии [Node-]».

После появления io.js значение process.release.name также может статьio.js (см. process-doc ).Для правильного определения среды, готовой к узлу, я думаю, вам следует проверить следующее:

Identify Node (> = 3.0.0) или io.js

(typeof process !== 'undefined') &&
(process.release.name.search(/node|io.js/) !== -1)

Этот оператор был протестирован с Node5.5.0, Electron 0.36.9 (с узлом 5.1.1) и Chrome 48.0.2564.116.

Идентифицирующий узел (> = 0.10.0) или io.js

(typeof process !== 'undefined') &&
(typeof process.versions.node !== 'undefined')

@Комментарий Далюге вдохновил меня подумать о более общем доказательстве.Это должно работать из Node.js> = 0.10 .Я не нашел уникального идентификатора для предыдущих версий.


Ps: я публикую этот ответ здесь, так как вопрос привел меня сюда, хотя ОП искал ответ на другойвопрос.

25 голосов
/ 12 августа 2012

Проблема с попыткой выяснить, в какой среде работает ваш код, заключается в том, что любой объект может быть изменен и объявлен, что делает практически невозможным выяснение того, какие объекты являются естественными для среды, а какие были изменены программой..

Однако есть несколько приемов, которые мы можем использовать, чтобы точно определить, в какой среде вы находитесь.

Давайте начнем с общепринятого решения, которое используется в библиотеке подчеркивания:

typeof module !== 'undefined' && module.exports

Этот метод на самом деле идеально подходит для серверной стороны, так как при вызове функции require он сбрасывает объект this в пустой объект и переопределяет module для вас снова, что означает, что вам не нужно беспокоиться о каких-либо внешних вмешательствах.Пока ваш код загружен с помощью require, вы в безопасности.

Однако в браузере это не работает, так как любой может легко определить module, чтобы он выглядел как объект, которым вы являетесьнаходясь в поиске.С одной стороны, это может быть желаемое поведение, но оно также определяет, какие переменные пользователь библиотеки может использовать в глобальной области видимости.Может быть, кто-то хочет использовать переменную с именем module, в которой есть exports, для другого использования.Это маловероятно, но кто мы такие, чтобы судить, какие переменные может использовать кто-то другой, просто потому, что другое имя использует эту переменную?

Однако хитрость в том, что если мы предполагаем, что ваш скрипт загружается в глобальныйОбласть (которая будет, если она загружена через тег script), переменная не может быть зарезервирована во внешнем закрытии, потому что браузер не позволяет этого.Теперь запомните в узле, объект this является пустым объектом, но переменная module все еще доступна.Это потому, что он объявлен во внешнем закрытии.Таким образом, мы можем исправить проверку подчеркивания, добавив дополнительную проверку:

this.module !== module

При этом, если кто-то объявит module в глобальной области видимости в браузере, он будет помещен вобъект this, который приведет к сбою теста, поскольку this.module будет тем же объектом, что и модуль.На узле this.module не существует, а module существует во внешнем замыкании, поэтому проверка будет успешной, поскольку они не эквивалентны.

Таким образом, окончательный тест:

typeof module !== 'undefined' && this.module !== module

Примечание. Хотя теперь это позволяет свободно использовать переменную module в глобальной области действия, все еще возможно обойти это в браузере, создав новое закрытие и объявив moduleв этом, затем загружая скрипт в этом замыкании.В этот момент пользователь полностью реплицирует среду узла и, надеюсь, знает, что он делает, и пытается выполнить требования стиля узла.Если код вызывается в теге скрипта, он все равно будет защищен от любых новых внешних замыканий.

20 голосов
/ 18 июня 2014

Следующее работает в браузере, если намеренно, явно не саботируется:

if(typeof process === 'object' && process + '' === '[object process]'){
    // is node
}
else{
    // not node
}

Bam.

13 голосов
/ 26 января 2017

Вот довольно крутой способ сделать это:

const isBrowser = this.window === this;

Это работает, потому что в браузерах глобальная переменная 'this' имеет собственную ссылку, называемую 'window'.Эта собственная ссылка не существует в узле.

  • В браузере «это» является ссылкой на глобальный объект, называемый «окном».
  • В узле «это» является ссылкой на объект module.exports.
    • 'this' является , а не ссылкой на глобальный объект Node, называемый 'global'.
    • 'this' является , а не ссылкой напространство объявления переменных модуля.

Чтобы прервать вышеупомянутую проверку браузера, вы должны сделать что-то вроде следующего

this.window = this;

перед выполнениемпроверьте.

10 голосов
/ 07 августа 2016

Еще одно обнаружение окружения :

(имеется в виду: большинство ответов здесь в порядке.)

function isNode() {
    return typeof global === 'object'
        && String(global) === '[object global]'
        && typeof process === 'object'
        && String(process) === '[object process]'
        && global === global.GLOBAL // circular ref
        // process.release.name cannot be altered, unlike process.title
        && /node|io\.js/.test(process.release.name)
        && typeof setImmediate === 'function'
        && setImmediate.length === 4
        && typeof __dirname === 'string'
        && Should I go on ?..
}

Немного параноидальноправо?Вы можете сделать это более многословным, проверив наличие globals .

Но не делайте! Он не будет привязан к исходному глобальному объекту узла, но будет прикреплен к объекту window в браузере.Так что это будет означать, что вы находитесь в Node env в браузере. Жизнь коротка!

Неужели нам все равно, если наша среда фальсифицирована?Это произойдет, когда какой-нибудь глупый разработчик объявит глобальную переменную с именем global в глобальной области видимости.Или какой-то злой разработчик как-то внедряет код в нашу среду.

Мы можем помешать выполнению нашего кода, когда мы его поймаем, но многие другие зависимости нашего приложения могут попасть в это.Так что в конечном итоге код сломается.Если ваш код достаточно хорош, вы не должны заботиться о каждой глупой ошибке, которая могла бы быть допущена другими.

И что?

Если нацелены на 2 среды: Browser и Node;
"use strict";и либо просто проверьте window или global;и четко указать, что в документации ваш код поддерживает только эти среды.Вот и все!

var isBrowser = typeof window !== 'undefined'
    && ({}).toString.call(window) === '[object Window]';

var isNode = typeof global !== "undefined" 
    && ({}).toString.call(global) === '[object global]';

Если возможно, для вашего случая использования;вместо обнаружения среды;сделать синхронное определение функции в блоке try / catch.(это займет несколько миллисекунд).

например

function isPromiseSupported() {
    var supported = false;
    try {
        var p = new Promise(function (res, rej) {});
        supported = true;
    } catch (e) {}
    return supported;
}
9 голосов
/ 05 августа 2013

Большинство предложенных решений могут быть подделаны. Надежным способом является проверка внутреннего свойства Class глобального объекта с использованием Object.prototype.toString. Внутренний класс не может быть подделан в JavaScript:

var isNode = 
    typeof global !== "undefined" && 
    {}.toString.call(global) == '[object global]';
4 голосов
/ 04 апреля 2013

Я использую process для проверки node.js, например, так:

if (typeof(process) !== 'undefined' && process.version === 'v0.9.9') {
  console.log('You are running Node.js');
} else {
  // check for browser
}

или

if (typeof(process) !== 'undefined' && process.title === 'node') {
  console.log('You are running Node.js');
} else {
  // check for browser
}

Документировано здесь

4 голосов
/ 13 апреля 2012

Вот мой вариант того, что выше:

(function(publish) {
    "use strict";

    function House(no) {
        this.no = no;
    };

    House.prototype.toString = function() {
        return "House #"+this.no;
    };

    publish(House);

})((typeof module == 'undefined' || (typeof window != 'undefined' && this == window))
    ? function(a) {this["House"] = a;}
    : function(a) {module.exports = a;});

Чтобы использовать его, вы изменяете «Дом» во второй последней строке, чтобы он был таким, каким вы хотите, чтобы имя модуля было в браузере,Публикуйте все, что хотите, чтобы значение модуля было (обычно конструктор или литерал объекта).

В браузерах глобальным объектом является окно, и у него есть ссылка на себя (есть окно window.window, которое== окно).Мне кажется, что это вряд ли произойдет, если вы не находитесь в браузере или в среде, которая хочет, чтобы вы верили, что вы в браузере.Во всех других случаях, если объявлена ​​глобальная переменная 'module', она использует ее, в противном случае она использует глобальный объект.

...