为什么{} + {}只能在客户端使用NaN? 为什么不在Node.js中?
当[] + []
是空string时, [] + {}
是"[object Object]"
, {} + []
是0
。 为什么是{} + {}
NaN?
> {} + {} NaN
我的问题不是为什么({} + {}).toString()
是"[object Object][object Object]"
而NaN.toString()
是"NaN"
, 这部分已经有答案了 。
我的问题是为什么这只发生在客户端? 在服务器端( Node.js ) {} + {}
是"[object Object][object Object]"
。
> {} + {} '[object Object][object Object]'
总结 :
在客户端:
[] + [] // Returns "" [] + {} // Returns "[object Object]" {} + [] // Returns 0 {} + {} // Returns NaN NaN.toString() // Returns "NaN" ({} + {}).toString() // Returns "[object Object][object Object]" var a = {} + {}; // 'a' will be "[object Object][object Object]"
在Node.js中:
[] + [] // Returns "" (like on the client) [] + {} // Returns "[object Object]" (like on the client) {} + [] // Returns "[object Object]" (not like on the client) {} + {} // Returns "[object Object][object Object]" (not like on the client)
更新说明: 这已在Chrome 49中修复 。
非常有趣的问题! 让我们来深入。
根本原因
差异的根源在于Node.js如何评估这些陈述与Chrome开发工具的效果。
什么Node.js做的
Node.js为此使用repl模块。
来自Node.js REPL源代码 :
self.eval( '(' + evalCmd + ')', self.context, 'repl', function (e, ret) { if (e && !isSyntaxError(e)) return finish(e); if (typeof ret === 'function' && /^[\r\n\s]*function/.test(evalCmd) || e) { // Now as statement without parens. self.eval(evalCmd, self.context, 'repl', finish); } else { finish(null, ret); } } );
这就像在Chrome开发者工具中运行({}+{})
,它也可以像你期望的那样生成"[object Object][object Object]"
。
Chrome开发者工具的function
另一方面, Chrome浏览器开发工具可以执行以下操作 :
try { if (injectCommandLineAPI && inspectedWindow.console) { inspectedWindow.console._commandLineAPI = new CommandLineAPI(this._commandLineAPIImpl, isEvalOnCallFrame ? object : null); expression = "with ((window && window.console && window.console._commandLineAPI) || {}) {\n" + expression + "\n}"; } var result = evalFunction.call(object, expression); if (objectGroup === "console") this._lastResult = result; return result; } finally { if (injectCommandLineAPI && inspectedWindow.console) delete inspectedWindow.console._commandLineAPI; }
所以基本上,它使用expression式对对象执行call
。 expression是:
with ((window && window.console && window.console._commandLineAPI) || {}) { {}+{};// <-- This is your code }
所以,正如你所看到的,expression式正在被直接评估,没有包装的括号。
为什么Node.js的行为不同
Node.js的来源certificate了这一点:
// This catches '{a : 1}' properly.
节点并不总是这样。 这是改变它的实际提交 。 Ryan对这个改变留下了下面的评论:“改进REPL命令如何被撤销”,举个例子。
犀牛
更新 – OP对Rhino的行为感兴趣(为什么它的行为像Chrome devtools,而不像nodejs)。
犀牛使用完全不同的JS引擎,不像Chrome开发者工具和Node.js的REPL,都使用V8。
以下是在Rhino shell中使用Rhino评估JavaScript命令时发生的基本pipe线。
-
该shell运行
org.mozilla.javascript.tools.shell.main
。 -
反过来,它会调用这个
new IProxy(IProxy.EVAL_INLINE_SCRIPT);
例如,如果代码直接通过内联开关-e传递。 -
这点击IProxy的
run
方法。 -
它调用
evalInlineScript
( src )。 这只是编译string和eval它。
基本上:
Script script = cx.compileString(scriptText, "<command>", 1, null); if (script != null) { script.exec(cx, getShellScope()); // <- just an eval }
犀牛的shell是三个最接近实际eval
的shell,没有任何包装。 犀牛是最接近实际的eval()
声明,你可以期望它的行为完全一样eval
会。