Why Is {} + {} No Longer Nan in Chrome Console

Why is {} + {} no longer NaN in Chrome console?

Chrome devtools now automatically wrap everything that begins with { and ends with } in an implicit pair of parentheses (see code), to force its evaluation as an expression. That way, {} creates an empty object now. You can see this if you go back through the history (), the previous line will be contained in (…).

Why? I don't know, but I could guess it reduces confusion for newbies that don't know of the block-vs-object-literal thing, and it's also more helpful if you just want to evaluate an expression.

And in fact that's the reasoning, as discussed in bug 499864. Pure convenience. And because node REPL had it as well (see code).

Why {} + {} is NaN only on the client side? Why not in Node.js?

Updated note: this has been fixed in Chrome 49.

Very interesting question! Let's dig in.

The root cause

The root of the difference is in how Node.js evaluates these statements vs. how the Chrome development tools do.

What Node.js does

Node.js uses the repl module for this.

From the Node.js REPL source code:

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);
}
}
);

This acts just like running ({}+{}) in the Chrome developer tools, which also produces "[object Object][object Object]" as you'd expect.

What the chrome developer tools do

On the other hand Chrome dveloper tools does the following:

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;
}

So basically, it performs a call on the object with the expression. The expression being:

with ((window && window.console && window.console._commandLineAPI) || {}) {
{}+{};// <-- This is your code
}

So, as you can see, the expression is being evaluted directly, without the wrapping parenthesis.

Why Node.js acts differently

Node.js's source justifies this:

// This catches '{a : 1}' properly.

Node did not always act like this. Here is the actual commit that changed it. Ryan left the following comment on the change: "Improve how REPL commands are evaled" with an example of the difference.


Rhino

Update - OP was interested in how Rhino behaves (and why it behaves like the Chrome devtools and unlike nodejs).

Rhino uses a completely different JS engine unlike the Chrome developer tools and Node.js's REPL which both use V8.

Here is the basic pipe line of what happens when you eval a JavaScript command with Rhino in the Rhino shell.

  • The shell runs org.mozilla.javascript.tools.shell.main.

  • In turn, it calls this new IProxy(IProxy.EVAL_INLINE_SCRIPT); for example, if the code was passed directly with the inline switch -e.

  • This hits IProxy's run method.

  • It invokes evalInlineScript (src). This simply compiles the string and evals it.

Basically:

Script script = cx.compileString(scriptText, "<command>", 1, null);
if (script != null) {
script.exec(cx, getShellScope()); // <- just an eval
}

Out of the three, Rhino's shell is the one that does the closest thing to an actual eval without any wrapping. Rhino's is the closest to an actual eval() statement and you can expect it to behave exactly like eval would.

Why JS object console log gives NaN instead of Number?

Change your property name to anything but "Charge".

For instance, Charge -> DeliveryCharge:

Delivery {
Id: 1,
ShippingDateTime: null,
ApproximateHandoverDateTime: null,
DeliveryCharge: 527,
Cost: null
}

If you try 9n**9n**9n in Chrome's console, Chrome breaks (it resembles an infinite loop). Why does this happen?

As was said already, 9n is the BigInt representation of 9.

The ** (power) operator works from right to left, causing quick escalation of results:

2n**2n**2n === 2n ** 4n === 16n
3n**3n**3n === 3n ** 27n === 7625597484987n
4n**4n**4n === 4n ** 256n === 13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084096n

On my system this becomes quite laggy from 7n**7n**7n, which takes about 32 seconds to calculate print. The result is 695976 digits, the first 5000 of which are printed in the console.

I haven't tried it any further, but I'd say it is just chewing away on the result. This could well take hours or days to calculate print (or perhaps even an Out Of Memory situation might occur at some point).

Update:

I just tried var x = 7n**7n**7n in the Chrome console, so just assigning it to a variable, and this finished in almost no time. It turns out that converting the bigint to a string is what takes up time; printing x.toString().length takes a similar amount of time as printing x or 7n**7n**7n.

Further experimenting revealed other interesting behaviour, see these results:

// Pure calculation time increases significantly when the exponent grows:
var x = 7n**7n**7n; // ~ 1200 ms
var x = 7n**8n**7n; // ~ 7000 ms
var x = 7n**7n**8n; // ~ 62000 ms
var x = 7n**8n**8n; // ~ 470000 ms

// But it's a different story when the base number is 'simple' in binary terms, e.g. 8n:
var x = 8n**7n**7n; // ~ 1 ms
var x = 8n**8n**7n; // ~ 1 ms
var x = 8n**7n**8n; // ~ 7 ms
var x = 8n**8n**8n; // ~ 17 ms

And yes, there is an end to it all:

var x = 32n**16n**8n;

gives:

VM436:1 Uncaught RangeError: Maximum BigInt size exceeded
at <anonymous>:1:28

The upper limit in Chrome appears to be 1 billion bits (1e9 bits), or about 125 MB - Reference: https://github.com/tc39/proposal-bigint/issues/174#issuecomment-437471065



Related Topics



Leave a reply



Submit