我有一个我需要从node.js脚本的脚本,我想保持javascript引擎独立。
所以,例如,我想做:
exports.x = y;
只有它在node.js下运行我该怎么办这个测试?
编辑:发布此问题时,我不知道node.js模块功能是基于commonjs。
对于具体的例子,我提出了一个更准确的问题:
脚本如何判断它是否需要作为commonjs模块?
通过寻找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._ = _;
}
})();
此处的示例保留Module模式。
脚本如何判断它是否需要作为commonjs模块?
相关:要检查是否已将模块作为模块直接在节点中运行,您可以检查require.main !== module
。 http://nodejs.org/docs/latest/api/modules.html#accessing_the_main_module
这是我对上述内容的变化:
(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;});
要使用它,您可以将第二行的“House”修改为您希望模块名称在浏览器中的任何内容,并发布您想要的模块值(通常是构造函数或对象文字) )。
在浏览器中,全局对象是窗口,它有自己的引用(有一个window.window,它是== window)。在我看来,除非您在浏览器中或希望您相信自己在浏览器中的环境中,否则不太可能发生这种情况。在所有其他情况下,如果声明了一个全局“模块”变量,它将使用它,否则它将使用全局对象。
我正在使用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
}
记录的here
Node.js有process
对象,所以只要你没有任何其他创建process
的脚本你可以使用它来确定代码是否在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");
}
这是一种非常安全和直接的方法,可确保服务器端和客户端javascript之间的兼容性,这也适用于包含客户端的browserify,RequireJS或CommonJS:
(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;
}())
编辑:关于您更新的问题:“脚本如何判断它是否需要作为commonjs模块?”我认为不可以。你可以检查exports
是否是一个对象(if (typeof exports === "object")
),因为the spec要求它提供给模块,但所有告诉你的是...... exports
是一个对象。 :-)
原始答案:
我确定有一些NodeJS特定的符号(
也许是EventEmitter
不,你必须使用require
来获取事件模块;你可以查看下面的内容,但正如大卫所说,理想情况下,如果有任何意义,你最好不要检测功能(而不是环境)。
更新:也许是这样的:
if (typeof require === "function"
&& typeof Buffer === "function"
&& typeof Buffer.byteLength === "function"
&& typeof Buffer.prototype !== "undefined"
&& typeof Buffer.prototype.write === "function") {
但这只是告诉你,你在require
的环境中,非常非常像NodeJS的Buffer
。 :-)
const isNode =
typeof process !== 'undefined' &&
process.versions != null &&
process.versions.node != null;
获取node.js的源代码并更改它以定义像runningOnNodeJS
这样的变量。检查代码中的变量。
如果您不能拥有自己的node.js私有版本,请在项目中打开功能请求。要求他们定义一个变量,该变量为您提供正在运行的node.js的版本。然后检查该变量。
很老的帖子,但我只是通过在try-catch中包装require语句来解决它
try {
var fs = require('fs')
} catch(e) {
alert('you are not in node !!!')
}
我觉得它很简单。
if (process && process.title === 'node') {
// Your code here
}
那么没有可靠的方法来检测Node.js中的运行,因为每个网站都可以很容易地声明相同的变量,但是,由于Node.js中没有window
对象,默认情况下你可以反过来检查你是否在里面运行浏览器。
这是我用于libs的东西,它们应该在浏览器和Node.js下工作:
if (typeof window === 'undefined') {
exports.foo = {};
} else {
window.foo = {};
}
如果在Node.js中定义了window
,它可能仍会爆炸,但是没有充分的理由让某人这样做,因为你明确需要省略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
。
我目前偶然发现错误的Node检测,由于误导性的特征检测,它不知道Electron中的节点环境。以下解决方案明确标识了流程环境。
(typeof process !== 'undefined') && (process.release.name === 'node')
这将发现您是否在节点进程中运行,因为process.release
包含“与当前[Node-]发布相关的元数据”。
在io.js产生之后,process.release.name
的值也可能变成io.js
(参见process-doc)。为了正确检测节点就绪环境,我猜你应该检查如下:
(typeof process !== 'undefined') &&
(process.release.name.search(/node|io.js/) !== -1)
该声明使用Node 5.5.0,Electron 0.36.9(带有Node 5.1.1)和Chrome 48.0.2564.116进行了测试。
(typeof process !== 'undefined') &&
(typeof process.versions.node !== 'undefined')
@daluege的评论激发了我思考更一般的证据。这应该来自Node.js> = 0.10。我没有找到以前版本的唯一标识符。
P.s。:我在这里发布了答案,因为问题引导我在这里,虽然OP正在寻找一个不同问题的答案。
试图弄清楚代码运行的环境的问题是,任何对象都可以被修改和声明,使得几乎无法确定哪些对象是环境本机的,哪些对象已被程序修改。
但是,我们可以使用一些技巧来确定您所处的环境。
让我们从下划线库中使用的普遍接受的解决方案开始:
typeof module !== 'undefined' && module.exports
这种技术实际上对于服务器端来说非常好,因为当调用require
函数时,它会将this
对象重置为空对象,并再次为您重新定义module
,这意味着您不必担心任何外部篡改。只要你的代码加载了require
,你就是安全的。
然而,这在浏览器上分崩离析,因为任何人都可以轻松定义module
,使其看起来像是你正在寻找的对象。一方面,这可能是您想要的行为,但它也决定了库用户可以在全局范围内使用哪些变量。也许有人想要使用名为module
的变量,其中包含exports
以供其他用途。它不太可能,但是我们要判断别人可以使用哪些变量,只是因为另一个环境使用该变量名称?
但是,如果我们假设您的脚本正在全局范围内加载(如果它是通过脚本标记加载的话),则无法在外部闭包中保留变量,因为浏览器不允许。现在请记住,在节点中,this
对象是一个空对象,但module
变量仍然可用。那是因为它在外部封闭中声明。因此,我们可以通过添加额外的检查来修复下划线的检查:
this.module !== module
有了这个,如果有人在浏览器的全局范围内声明module
,它将被放置在this
对象中,这将导致测试失败,因为this.module
将与模块相同。在节点上,this.module
不存在,并且module
存在于外部闭包中,因此测试将成功,因为它们不相等。
因此,最终的测试是:
typeof module !== 'undefined' && this.module !== module
注意:虽然现在允许module
变量在全局范围内自由使用,但仍然可以通过创建一个新的闭包并在其中声明module
,然后在该闭包内加载脚本来绕过浏览器。此时,用户正在完全复制节点环境,并希望知道他们正在做什么,并且正在尝试执行节点样式要求。如果在脚本标记中调用代码,则对于任何新的外部闭包仍然是安全的。
以下是在浏览器中工作,除非故意,明确地破坏:
if(typeof process === 'object' && process + '' === '[object process]'){
// is node
}
else{
// not node
}
巴姆。
这也是一个非常酷的方式:
const isBrowser = this.window === this;
这是有效的,因为在浏览器中,全局'this'变量有一个名为'window'的自引用。 Node中不存在此自引用。
要打破上面建议的浏览器检查,您必须执行以下操作
this.window = this;
在执行检查之前。
又一个环境检测:
(意思是:这里的大多数答案都没问题。)
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来使这个更加冗长。
无论如何,以上所有这些都可以伪造/模拟。
例如,伪造global
对象:
global = {
toString: function () {
return '[object global]';
},
GLOBAL: global,
setImmediate: function (a, b, c, d) {}
};
setImmediate = function (a, b, c, d) {};
...
这不会附加到Node的原始全局对象,但会在浏览器中附加到window
对象。所以它意味着你在浏览器中的Node env中。
如果我们的环境是伪造的,我们是否在意?当一些愚蠢的开发人员在全局范围内声明一个名为global
的全局变量时,就会发生这种情况。或者某些邪恶的开发者以某种方式在我们的环境中注入代码。
当我们捕获这个时,我们可能会阻止我们的代码执行,但我们的应用程序的许多其他依赖项可能会被捕获。所以最终代码会破裂。如果你的代码足够好,你就不应该关心别人可能做过的每一个愚蠢的错误。
如果定位2个环境:浏览器和节点;
"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块中执行同步特征检测。 (这些将花费几毫秒来执行)。
EG
function isPromiseSupported() {
var supported = false;
try {
var p = new Promise(function (res, rej) {});
supported = true;
} catch (e) {}
return supported;
}
大多数提议的解决方案实际上都可以伪造。一种强大的方法是使用Class
检查全局对象的内部Object.prototype.toString
属性。内部类不能在JavaScript中伪造:
var isNode =
typeof global !== "undefined" &&
{}.toString.call(global) == '[object global]';
如何使用过程对象并检查execPath用于node
?
process.execPath
这是启动该进程的可执行文件的绝对路径名。
例:
在/ usr / local / bin目录/节点