Neden {} + {} NaN sadece müşteri tarafında? Neden Node.js'de olmasın?


136

İken [] + [], boş bir dizedir [] + {}olduğunu "[object Object]"ve {} + []bir 0. {} + {}NaN neden ?

> {} + {}
  NaN

Neden Sorum değil ({} + {}).toString()ise "[object Object][object Object]"ise NaN.toString()ise "NaN", bu kısım zaten burada bir cevabı var .

Sorum şu: Bu neden sadece müşteri tarafında oluyor? Sunucu tarafında ( node.js ) {} + {}olduğunu "[object Object][object Object]".

> {} + {}
'[object Object][object Object]'

Özetleme :

Müşteri tarafında:

 [] + []              // 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'de:

 [] + []   // 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)

4
Bunu yapan sadece tarayıcı konsolu. Yapmayı deneyin için konsola ve NodeJS içinde aynı. jsbin.com/oveyuj/1/edit
elclanrs

2
Gerçekten bir kopya değil, NodeJS cevabı istiyorum.
Yeniden

4
Hmm ... üzgünüm. Bununla birlikte, stackoverflow.com/questions/9032856/… hala alakalı ve ilk yarıyı cevaplıyor
John Dvorak

3
{}İçeriğe bağlı olarak bunun bir ifade veya ilkel bir nesne olarak yorumlanabileceğini unutmayın . Belki kod istemcide ve sunucuda aynıdır, ancak {}kodun girilmesinin farklı bağlamı nedeniyle farklı yorumlanmaktadır .
Patashu

18
Lütfen bu soruyu gerçekten tekrarlamadığınız için tekrar tekrar açın ve bu soruyu kapatmayı bırakın .
Alvin Wong

Yanıtlar:


132

Güncellenmiş not: Bu, Chrome 49'da düzeltildi .

Çok ilginç bir soru! Hadi kazalım.

Temel neden

Farkın kökü, Node.js'nin bu açıklamaları nasıl değerlendirdiği ve Chrome geliştirme araçlarının nasıl işlediğidir.

Node.js ne yapar

Node.js bunun için repl modülünü kullanır .

Node.js REPL kaynak kodundan :

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

Bu, tıpkı beklediğiniz gibi ({}+{})üretilen Chrome geliştirici araçlarında çalışmak gibi davranır "[object Object][object Object]".

Chrome geliştirici araçlarının yaptıkları

Öte yandan Chrome dveloper araçları şunları yapar :

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

Temel olarak, callifadeyle birlikte nesne üzerinde bir a gerçekleştirir . İfade şu şekildedir:

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

Gördüğünüz gibi, ifade sarma parantezi olmadan doğrudan değerlendiriliyor.

Node.js neden farklı davranıyor

Node.js kaynağı bunu haklı çıkarır:

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

Düğüm her zaman böyle davranmadı. İşte onu değiştiren gerçek taahhüt . Ryan değişiklik hakkında şu yorumu yaptı: "REPL komutlarının değerlendirilme şeklini geliştirin" farkına bir örnekle.


Gergedan

Güncelleme - OP, Rhino'nun nasıl davrandığıyla (ve neden Chrome aygıtları gibi ve düğümlerin aksine) davrandığıyla ilgileniyordu.

Rhino, Chrome geliştirici araçlarından ve her ikisi de V8 kullanan Node.js'nin REPL'sinden farklı olarak tamamen farklı bir JS motoru kullanıyor.

İşte Rhino kabuğundaki Rhino ile bir JavaScript komutu değerlendirdiğinizde gerçekleşen temel boru hattı.

Temelde:

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

Üçünden Rhino'nun kabuğu, evalherhangi bir sargı olmadan bir gerçeğe en yakın şeyi yapan kabuktur . Gergedan, gerçek bir eval()ifadeye en yakın olanıdır ve tam olarak olduğu gibi davranmasını bekleyebilirsiniz eval.


1
(Cevabın gerçekten bir parçası değil, ancak bahsetmeye değer nodejs, sadece bir JavaScript değil, REPL kullanırken varsayılan olarak değerlendirmek için vm modülünü kullanıyor eval)
Benjamin Gruenbaum

Örneğin, gergedanın Terminal'de neden aynı şeyi yaptığını açıklayabilir misiniz (sadece Chrome Konsolu değil)?
Ionică Bizău

5
Mümkünse +10! Vay canına, ... Gerçekten hayatın yok ya da böyle bir şey bilmek benden daha zekisin. Lütfen, bu cevabı bulmak için biraz aradığınızı söyleyin :)
Samuel

7
@Samuel Tek gereken kaynak okumak - yemin ederim! Chrome'da, 'debugger' yazarsanız; , tüm boruyu alırsınız - sizi yukarıda sadece bir işlevle doğrudan 'ile' atar evaluateOn. Düğümde, her şey çok iyi belgelenmiş - Git'te güzel ve rahat tüm geçmişe sahip özel bir REPL modülü var, daha önce kendi programımda REPL'leri kullandım, nereye bakacağımı biliyordum :) Sevdiğinize ve bulduğunuza sevindim yararlı, ama ben bu kod üsleri (dev-tools ve nodejs) benim akıl yerine aşina borçluyum. Doğrudan kaynağa gitmek çoğu zaman en kolay olanıdır.
Benjamin Gruenbaum

Güncelleme - Chrome'daki konsol API'sı biraz güncellendi, bu nedenle buradaki genel fikir doğru olsa da, yayınlanan kod Chrome'un en son sürümü için doğru değil. Bkz chromium.googlesource.com/chromium/blink.git/+/master/Source/...
Benjamin Gruenbaum
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.