我有一个需要来自 Node.js 脚本的脚本,我希望它保持 JavaScript 引擎独立。
例如,我只想exports.x = y;
在它在 Node.js 下运行时才这样做。我该如何执行此测试?
在发布这个问题时,我不知道 Node.js module功能是基于CommonJS 的。
对于我给出的具体示例,更准确的问题是:
脚本如何判断它是否需要作为 CommonJS module?
我有一个需要来自 Node.js 脚本的脚本,我希望它保持 JavaScript 引擎独立。
例如,我只想exports.x = y;
在它在 Node.js 下运行时才这样做。我该如何执行此测试?
在发布这个问题时,我不知道 Node.js module功能是基于CommonJS 的。
对于我给出的具体示例,更准确的问题是:
脚本如何判断它是否需要作为 CommonJS module?
好吧,没有可靠的方法来检测 Node.js 中的运行情况,因为每个网站都可以轻松声明相同的变量,但是,由于window
默认情况下 Node.js 中没有对象,您可以反过来检查您是否在内部运行浏览器。
这是我用于应该在浏览器和 Node.js 下都可以工作的库的内容:
if (typeof window === 'undefined') {
exports.foo = {};
} else {
window.foo = {};
}
如果它window
在 Node.js 中定义,它可能仍然会爆炸,但没有充分的理由让某人这样做,因为您需要明确地省略var
或设置global
对象的属性。
编辑
要检测您的脚本是否需要作为 CommonJS module,这又不是一件容易的事。commonJS 唯一指定的是 A:module将通过对函数的调用包含在内,require
而 B:module通过exports
对象上的属性导出事物。现在如何实现留给底层系统。Node.js 将module的内容包装在一个匿名函数中:
function (exports, require, module, __filename, __dirname) {
参见:https : //github.com/ry/node/blob/master/src/node.js#L325
但是不要试图通过一些疯狂的arguments.callee.toString()
东西来检测它,而只是使用我上面检查浏览器的示例代码。Node.js 是一种更清洁的环境,因此不太可能window
在那里声明。
通过寻找 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模式。
我目前偶然发现了错误的 Node 检测,由于误导性的特征检测,它不知道Electron中的 Node 环境。以下解决方案明确标识流程环境。
(typeof process !== 'undefined') && (process.release.name === 'node')
这将发现您是否在 Node 进程中运行,因为process.release
包含“与当前 [Node-] 版本相关的元数据”。
在io.js产生之后,的值process.release.name
也可能变成io.js
(参见process-doc)。为了正确检测 Node-ready 环境,我想你应该检查如下:
(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。我没有找到以前版本的唯一标识符。
Ps:我在这里发布了这个答案,因为这个问题把我带到了这里,尽管 OP 正在寻找另一个问题的答案。
试图弄清楚你的代码在什么环境中运行的问题在于,任何对象都可以被修改和声明,这使得几乎不可能确定哪些对象是环境本机的,哪些已经被程序修改了。
但是,我们可以使用一些技巧来确定您所处的环境。
让我们从下划线库中使用的普遍接受的解决方案开始:
typeof module !== 'undefined' && module.exports
这种技术对于服务器端实际上非常好,因为当require
调用该函数时,它会将this
对象重置为空对象,然后module
再次为您重新定义,这意味着您不必担心任何外部篡改。只要您的代码加载了require
,您就是安全的。
但是,这在浏览器上会失效,因为任何人都可以轻松定义module
使其看起来像是您正在寻找的对象。一方面,这可能是您想要的行为,但它也决定了库用户可以在全局范围内使用哪些变量。也许有人想将一个名称module
包含exports
在其中的变量用于其他用途。这不太可能,但是我们凭什么来判断其他人可以使用哪些变量,仅仅因为另一个环境使用该变量名称?
然而,诀窍是,如果我们假设您的脚本是在全局范围内加载的(如果它是通过脚本标签加载的),则不能在外部闭包中保留变量,因为浏览器不允许这样做. 现在记住在节点中,this
对象是一个空对象,但module
变量仍然可用。那是因为它是在外部闭包中声明的。所以我们可以通过添加一个额外的检查来修复下划线的检查:
this.module !== module
有了这个,如果有人module
在浏览器中的全局范围内声明,它就会被放置在this
对象中,这将导致测试失败,因为this.module
, 将是与module相同的对象。在节点上,this.module
不存在,并且module
存在于外部闭包中,因此测试将成功,因为它们不等价。
因此,最终的测试是:
typeof module !== 'undefined' && this.module !== module
注意:虽然这现在允许module
在全局范围内自由使用变量,但仍然可以通过创建一个新的闭包并module
在其中声明,然后在该闭包中加载脚本来绕过浏览器上的这一点。在这一点上,用户正在完全复制节点环境,并希望知道他们在做什么并且正在尝试执行节点样式要求。如果代码在脚本标签中被调用,它对于任何新的外部闭包仍然是安全的。
除非有意,明确破坏,否则以下内容在浏览器中有效:
if(typeof process === 'object' && process + '' === '[object process]'){
// is node
}
else{
// not node
}
砰。