ทำไม {} + {} เป็น NaN เฉพาะฝั่งไคลเอ็นต์เท่านั้น ทำไมไม่ใช้ Node.js?


136

ในขณะที่[] + []เป็นสตริงว่าง[] + {}เป็น"[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)

4
มันเป็นเพียงคอนโซลของเบราว์เซอร์ที่ทำเช่นนั้น ลองเข้าระบบเพื่อคอนโซลและก็เช่นเดียวกับใน NodeJS jsbin.com/oveyuj/1/edit
elclanrs

2
ไม่ซ้ำกันจริงๆฉันขอคำตอบ NodeJS การลงคะแนนเพื่อเปิดใหม่ ...
IonicăBizău

4
อืม ... ขอโทษ อย่างไรก็ตามstackoverflow.com/questions/9032856/…ยังคงมีความเกี่ยวข้องและตอบคำถามในครึ่งแรก
John Dvorak

3
อย่าลืมว่า{}สามารถตีความได้ว่าเป็นนิพจน์หรือเป็นวัตถุดั้งเดิมโดยขึ้นอยู่กับบริบท อาจเป็นรหัสเดียวกันบนไคลเอนต์และบนเซิร์ฟเวอร์ แต่มันตีความ{}แตกต่างกันเนื่องจากบริบทที่แตกต่างกันของการป้อนรหัส
Patashu

18
โปรดเปิดอีกครั้งแล้วหยุดปิดคำถามนี้ซ้ำแล้วซ้ำอีกเนื่องจากคำถามนี้ไม่เหมือนกันจริง ๆ
อัลวินหว่อง

คำตอบ:


132

อัปเดตหมายเหตุ: สิ่งนี้ได้รับการแก้ไขใน Chrome 4949

คำถามที่น่าสนใจมาก! มาขุดกัน

สาเหตุที่แท้จริง

รากของความแตกต่างคือวิธีที่ Node.js ประเมินข้อความเหล่านี้กับวิธีที่เครื่องมือในการพัฒนาของ Chrome ทำ

Node.js ทำอะไรได้บ้าง

Node.js ใช้โมดูลreplสำหรับสิ่งนี้

จากซอร์สโค้ด REPL ของ Node.js :

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 ทำอะไร

ในทางกลับกันเครื่องมือ Chrome dveloper ทำสิ่งต่อไปนี้ :

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

ดังนั้นโดยพื้นฐานแล้วมันจะทำ a callบนวัตถุด้วยนิพจน์ การแสดงออกเป็น:

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

ดังนั้นอย่างที่คุณเห็นการแสดงออกจะถูกประเมินโดยตรงโดยไม่ต้องใส่วงเล็บปิด

ทำไม Node.js ทำหน้าที่แตกต่าง

แหล่งที่มาของ Node.js แสดงเหตุผลนี้:

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

โหนดไม่ได้ทำตัวแบบนี้เสมอไป ที่นี่เป็นที่ที่เกิดขึ้นจริงกระทำที่มีการเปลี่ยนแปลงมัน Ryan ออกความคิดเห็นต่อไปนี้เกี่ยวกับการเปลี่ยนแปลง: "ปรับปรุงวิธีการที่คำสั่ง REPL จะถูก evaled" พร้อมด้วยตัวอย่างของความแตกต่าง


แรด

อัปเดต - OP มีความสนใจในพฤติกรรมของแรด (และทำไมมันมีพฤติกรรมเช่น devtools ของ Chrome และต่างจาก nodejs)

Rhino ใช้เครื่องมือ JS ที่แตกต่างอย่างสิ้นเชิงซึ่งแตกต่างจากเครื่องมือสำหรับนักพัฒนา Chrome และ REPL ของ Node.js ซึ่งทั้งคู่ใช้ V8

นี่คือบรรทัดไปป์พื้นฐานของสิ่งที่เกิดขึ้นเมื่อคุณประเมินคำสั่ง JavaScript กับ Rhino ในเปลือก Rhino

  • org.mozilla.javascript.tools.shell.mainเปลือกวิ่ง

  • ในทางกลับกันมันเรียกสิ่งนี้ new IProxy(IProxy.EVAL_INLINE_SCRIPT);เช่นถ้ารหัสถูกส่งโดยตรงกับ inline switch -e

  • นี่เป็นrunวิธีการของ IProxy

  • มันเรียกใช้evalInlineScript( src ) สิ่งนี้จะรวบรวมสตริงและ evals มัน

โดยทั่วไป:

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

เปลือกของแรดเป็นหนึ่งในสามที่ทำสิ่งที่ใกล้เคียงกับของจริงมากที่สุดevalโดยไม่มีการพัน แรดเป็นที่ใกล้เคียงกับที่เกิดขึ้นจริงeval()คำสั่งและคุณสามารถคาดหวังว่ามันจะทำตัวเหมือนevalหากว่า


1
(ไม่ใช่ส่วนหนึ่งของคำตอบ แต่ควรกล่าวถึง nodejs ใช้โมดูล vmสำหรับการวิวัฒนาการตามค่าเริ่มต้นเมื่อใช้ REPL ไม่ใช่ JavaScript eval)
Benjamin Gruenbaum

คุณช่วยอธิบายได้ไหมว่าทำไมแรดเช่นเดียวกันใน Terminal (ไม่ใช่แค่คอนโซล Chrome)?
IonicăBizău

5
+10 ถ้าเป็นไปได้! ว้าวชาย ... คุณไม่มีชีวิตหรือคุณฉลาดกว่าฉันจริงๆที่จะรู้อะไรแบบนั้น โปรดบอกฉันว่าคุณค้นหาคำตอบนี้เล็กน้อย :)
ซามูเอล

7
@ Samuel ทั้งหมดที่ได้อ่านคือแหล่งข่าว - ฉันสาบาน! ใน Chrome หากคุณป้อน 'ดีบักเกอร์' คุณจะได้รับทั้งท่อ - มันจะโยนคุณโดยตรงกับ 'กับ' evaluateOnที่มีเพียงหนึ่งฟังก์ชั่นด้านบนเพื่อ ในโหนดทุกอย่างได้รับการบันทึกไว้เป็นอย่างดี - พวกเขามีโมดูล REPL โดยเฉพาะที่มีประวัติดีและสบายบนคอมไพล์โดยใช้ REPLs ก่อนหน้านี้ในโปรแกรมของฉันเองฉันรู้ว่าต้องมองที่ไหน :) ฉันดีใจที่คุณชอบและพบ มันมีประโยชน์ แต่ฉันเป็นหนี้กับความคุ้นเคยกับฐานรหัสเหล่านี้ (เครื่องมือ dev และ nodejs) มากกว่าสติปัญญาของฉัน การไปยังแหล่งกำเนิดมักจะง่ายที่สุดเสมอ
Benjamin Gruenbaum

อัปเดต - คอนโซล API ใน Chrome ได้รับการอัปเดตเล็กน้อยดังนั้นในขณะที่แนวคิดทั่วไปที่นี่ถูกต้องรหัสที่โพสต์ไม่ถูกต้องสำหรับ Chrome เวอร์ชันล่าสุด ดูchromium.googlesource.com/chromium/blink.git/+/master/Source/…
Benjamin Gruenbaum
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.