可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a script I am requiring from a Node.js script, which I want to keep JavaScript engine independent.
For example, I want to do exports.x = y;
only if it’s running under Node.js. How can I perform this test?
When posting this question, I didn’t know the Node.js modules feature is based on CommonJS.
For the specific example I gave, a more accurate question would’ve been:
How can a script tell whether it has been required as a CommonJS module?
回答1:
By looking for CommonJS support, this is how the Underscore.js library does it:
Edit: to your updated question:
(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._ = _;
}
})();
Example here retains the Module pattern.
回答2:
Well there's no reliable way to detect running in Node.js since every website could easily declare the same variables, yet, since there's no window
object in Node.js by default you can go the other way around and check whether you're running inside a Browser.
This is what I use for libs that should work both in a Browser and under Node.js:
if (typeof window === 'undefined') {
exports.foo = {};
} else {
window.foo = {};
}
It might still explode in case that window
is defined in Node.js but there's no good reason for someone do this, since you would explicitly need to leave out var
or set the property on the global
object.
EDIT
For detecting whether your script has been required as a CommonJS module, that's again not easy. Only thing commonJS specifies is that A: The modules will be included via a call to the function require
and B: The modules exports things via properties on the exports
object. Now how that is implement is left to the underlying system. Node.js wraps the module's content in an anonymous function:
function (exports, require, module, __filename, __dirname) {
See: https://github.com/ry/node/blob/master/src/node.js#L325
But don't try to detect that via some crazy arguments.callee.toString()
stuff, instead just use my example code above which checks for the Browser. Node.js is a way cleaner environment so it's unlikely that window
will be declared there.
回答3:
I currently stumbled over a wrong detection of Node which is not aware of the Node-environment in Electron due to a misleading feature-detection. The following solutions identify the process-environment explicitly.
Identify Node.js only
(typeof process !== 'undefined') && (process.release.name === 'node')
This will discover if you're running in a Node-process, since process.release
contains the "metadata related to the current [Node-]release".
After the spawn of io.js the value of process.release.name
may also become io.js
(see the process-doc). To proper detect a Node-ready environment i guess you should check as follows:
Identify Node (>= 3.0.0) or io.js
(typeof process !== 'undefined') &&
(process.release.name.search(/node|io.js/) !== -1)
This statement was tested with Node 5.5.0, Electron 0.36.9 (with Node 5.1.1) and Chrome 48.0.2564.116.
Identify Node (>= 0.10.0) or io.js
(typeof process !== 'undefined') &&
(typeof process.versions.node !== 'undefined')
@daluege's comment inspired me to think about a more general proof. This should working from Node.js >= 0.10. I didn't find a unique identifier for prior versions.
P.s.: I am posting that answer here since the question lead me here, although the OP was searching for an answer to a different question.
回答4:
The problem with trying to figure out what environment your code is running in is that any object can be modified and declared making it close to impossible to figure out which objects are native to the environment, and which have been modified by the program.
However, there are a few tricks we can use to figure out for sure what environment you are in.
Lets start out with the generally accepted solution that's used in the underscore library:
typeof module !== 'undefined' && module.exports
This technique is actually perfectly fine for the server side, as when the require
function is called, it resets the this
object to an empty object, and redefines module
for you again, meaning you don't have to worry about any outside tampering. As long as your code is loaded in with require
, you are safe.
However, this falls apart on the browser, as anyone can easily define module
to make it seem like it's the object you are looking for. On one hand this might be the behavior you want, but it also dictates what variables the library user can use in the global scope. Maybe someone wants to use a variable with the name module
that has exports
inside of it for another use. It's unlikely, but who are we to judge what variables someone else can use, just because another environment uses that variable name?
The trick however, is that if we are assuming that your script is being loaded in the global scope (which it will be if it's loaded via a script tag) a variable cannot be reserved in an outer closure, because the browser does not allow that. Now remember in node, the this
object is an empty object, yet, the module
variable is still available. That is because it's declared in an outer closure. So we can then fix underscore's check by adding an extra check:
this.module !== module
With this, if someone declares module
in the global scope in the browser, it will be placed in the this
object, which will cause the test to fail, because this.module
, will be the same object as module. On node, this.module
does not exist, and module
exists within an outer closure, so the test will succeed, as they are not equivalent.
Thus, the final test is:
typeof module !== 'undefined' && this.module !== module
Note: While this now allows the module
variable to be used freely in the global scope, it is still possible to bypass this on the browser by creating a new closure and declaring module
within that, then loading the script within that closure. At that point the user is fully replicating the node environment and hopefully knows what they are doing and is trying to do a node style require. If the code is called in a script tag, it will still be safe from any new outer closures.
回答5:
The following works in the browser unless intentionally,explicitly sabotaged:
if(typeof process === 'object' && process + '' === '[object process]'){
// is node
}
else{
// not node
}
Bam.
回答6:
Here's a pretty cool way to do it as well:
const isBrowser = this.window === this;
This works because in browsers the global 'this' variable has a self reference called 'window'. This self reference is not existent in Node.
- In the browser 'this' is a reference to the global object, called 'window'.
- In Node 'this' is a reference to the module.exports
object.
- 'this' is not a reference to the Node global object, called 'global'.
- 'this' is not a reference to the the module variable declaration space.
To break the above suggested browser check you would have to do something like the following
this.window = this;
before executing the check.
回答7:
Yet another environment detection:
(Meaning: most of the answers here are alright.)
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 ?..
}
A bit paranoid right? You can make this more verbose by checking for more globals.
But DON'T!.
All these above can be faked/simulated anyway.
For example to fake the global
object:
global = {
toString: function () {
return '[object global]';
},
GLOBAL: global,
setImmediate: function (a, b, c, d) {}
};
setImmediate = function (a, b, c, d) {};
...
This won't get attached to the Node's original global object but it will get attached to the window
object in a browser. So it'll imply that you're in Node env inside a browser.
Life is short!
Do we care if our environment is faked? It'd happen when some stupid developer declare a global variable called global
in the global scope. Or some evil dev injects code in our env somehow.
We may prevent our code from executing when we catch this but lots of other dependencies of our app might get caught into this. So eventually the code will break. If your code is good enough, you should not care for each and every silly mistake that could have been done by others.
So what?
If targeting 2 environments: Browser and Node;
"use strict"
; and either simply check for window
or global
; and clearly indicate that in the docs that your code supports only these environments. That's it!
var isBrowser = typeof window !== 'undefined'
&& ({}).toString.call(window) === '[object Window]';
var isNode = typeof global !== "undefined"
&& ({}).toString.call(global) === '[object global]';
If possible for your use case; instead of environment detection; do synchronous feature detection within a try/catch block. (these will take a few milliseconds to execute).
e.g.
function isPromiseSupported() {
var supported = false;
try {
var p = new Promise(function (res, rej) {});
supported = true;
} catch (e) {}
return supported;
}
回答8:
Most of the proposed solutions can actually be faked. A robust way is to check the internal Class
property of the global object using the Object.prototype.toString
. The internal class can't be faked in JavaScript:
var isNode =
typeof global !== "undefined" &&
{}.toString.call(global) == '[object global]';
回答9:
What about using the process object and checking execPath for node
?
process.execPath
This is the absolute
pathname of the executable that
started the process.
Example:
/usr/local/bin/node
回答10:
How can a script tell whether it has been required as a commonjs module?
Related: to check whether it has been required as a module vs run directly in node, you can check require.main !== module
.
http://nodejs.org/docs/latest/api/modules.html#accessing_the_main_module
回答11:
Here's my variation on what's above:
(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;});
To use it, you modify the "House" on the second last line to be whatever you want the name of the module to be in the browser and publish whatever you want the value of the module to be (usually a constructor or an object literal).
In browsers the global object is window, and it has a reference to itself (there's a window.window which is == window). It seems to me that this is unlikely to occur unless you're in a browser or in an environment that wants you to believe you're in a browser. In all other cases, if there is a global 'module' variable declared, it uses that otherwise it uses the global object.
回答12:
I'm using process
to check for node.js like so
if (typeof(process) !== 'undefined' && process.version === 'v0.9.9') {
console.log('You are running Node.js');
} else {
// check for browser
}
or
if (typeof(process) !== 'undefined' && process.title === 'node') {
console.log('You are running Node.js');
} else {
// check for browser
}
Documented here
回答13:
Node.js has process
object, so as long as You don't have any other script which create process
You can use it to determine if code runs on Node.
var isOnNodeJs = false;
if(typeof process != "undefined") {
isOnNodeJs = true;
}
if(isOnNodeJs){
console.log("you are running under node.js");
}
else {
console.log("you are NOT running under node.js");
}
回答14:
This is a pretty safe and straight-forward way of assuring compatibility between server-side and client-side javascript, that will also work with browserify, RequireJS or CommonJS included client-side:
(function(){
// `this` now refers to `global` if we're in NodeJS
// or `window` if we're in the browser.
}).call(function(){
return (typeof module !== "undefined" &&
module.exports &&
typeof window === 'undefined') ?
global : window;
}())
回答15:
Edit: Regarding your updated question: "How can a script tell whether it has been required as a commonjs module?" I don't think it can. You can check whether exports
is an object (if (typeof exports === "object")
), since the spec requires that it be provided to modules, but all that tells you is that ... exports
is an object. :-)
Original answer:
I'm sure there's some NodeJS-specific symbol (EventEmitter
, perhaps no, you have to use require
to get the events module; see below) that you could check for, but as David said, ideally you're better off detecting the feature (rather than environment) if it makes any sense to do so.
Update: Perhaps something like:
if (typeof require === "function"
&& typeof Buffer === "function"
&& typeof Buffer.byteLength === "function"
&& typeof Buffer.prototype !== "undefined"
&& typeof Buffer.prototype.write === "function") {
But that just tells you that you're in an environment with require
and something very, very much like NodeJS's Buffer
. :-)
回答16:
const isNode =
typeof process !== 'undefined' &&
process.versions != null &&
process.versions.node != null;
回答17:
From the source of debug package:
const isBrowser = typeof process === 'undefined' || process.type === 'renderer' || process.browser === true || process.__nwjs
https://github.com/visionmedia/debug/blob/master/src/index.js#L6
回答18:
Take the source of node.js and change it to define a variable like runningOnNodeJS
. Check for that variable in your code.
If you can't have your own private version of node.js, open a feature request in the project. Ask that they define a variable which gives you the version of node.js that you're running in. Then check for that variable.
回答19:
Very old post, but i just solved it by wrapping the require statements in a try - catch
try {
var fs = require('fs')
} catch(e) {
alert('you are not in node !!!')
}
回答20:
I think its quite simple.
if (typeof process !== 'undefined' process && process.title === 'node') {
// Your code here
}