Tại sao {} + {} chỉ là NaN ở phía máy khách? Tại sao không có trong Node.js?


136

Trong khi [] + []là một chuỗi rỗng, [] + {}"[object Object]", và {} + []0. Tại sao lại là {} + {}NaN?

> {} + {}
  NaN

Câu hỏi của tôi không phải là lý do tại sao ({} + {}).toString()"[object Object][object Object]"trong khi NaN.toString()"NaN", phần này có một câu trả lời ở đây đã .

Câu hỏi của tôi là tại sao điều này chỉ xảy ra ở phía khách hàng? Về phía máy chủ ( Node.js ) {} + {}"[object Object][object Object]".

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

Tóm tắt :

Về phía khách hàng:

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

Trong 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
Nó chỉ là bảng điều khiển trình duyệt làm điều đó. Hãy thử đăng nhập vào bảng điều khiển và nó giống như trong NodeJS. jsbin.com/oveyuj/1/edit
elclanrs

2
Không thực sự là một bản sao, tôi đang yêu cầu câu trả lời của NodeJS. Bỏ phiếu để mở lại ...
Ionică Bizău

4
Hừm ... xin lỗi. Tuy nhiên, stackoverflow.com/questions/9032856/ Hy vẫn có liên quan và trả lời nửa đầu
John Dvorak

3
Đừng quên rằng {}có thể được hiểu là một biểu thức hoặc là một đối tượng nguyên thủy tùy thuộc vào ngữ cảnh. Có thể mã giống nhau trên máy khách và máy chủ nhưng nó diễn giải {}khác nhau do bối cảnh nhập mã khác nhau.
Patashu

18
Vui lòng mở lại và sau đó dừng đóng câu hỏi này nhiều lần vì câu hỏi này thực sự không phải là một bản sao .
Alvin Wong

Câu trả lời:


132

Lưu ý cập nhật: điều này đã được sửa trong Chrome 49 .

Câu hỏi rất thú vị! Nào cùng đào vào bên trong.

Nguyên nhân sâu xa

Căn nguyên của sự khác biệt là cách Node.js đánh giá các tuyên bố này so với cách các công cụ phát triển Chrome thực hiện.

Node.js làm gì

Node.js sử dụng repl mô-đun cho việc này.

Từ mã nguồn REPL của 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);
        }
    }
);

Điều này hoạt động giống như chạy ({}+{})trong các công cụ dành cho nhà phát triển Chrome, cũng tạo ra "[object Object][object Object]"như bạn mong đợi.

Các công cụ phát triển chrome làm gì

Mặt khác, các công cụ dveloper của Chrome thực hiện như sau :

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

Về cơ bản, nó thực hiện một calltrên đối tượng với biểu thức. Biểu thức là:

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

Vì vậy, như bạn có thể thấy, biểu thức đang được đánh giá trực tiếp, không có dấu ngoặc đơn.

Tại sao Node.js hoạt động khác nhau

Nguồn của Node.js biện minh cho điều này:

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

Nút không phải lúc nào cũng hành động như thế này. Đây là cam kết thực tế đã thay đổi nó . Ryan đã để lại bình luận sau đây về sự thay đổi: "Cải thiện cách các lệnh REPL được đánh giá cao" với một ví dụ về sự khác biệt.


Tê giác

Cập nhật - OP quan tâm đến cách Rhino hành xử (và tại sao nó hoạt động như các devtools của Chrome và không giống như nodejs).

Rhino sử dụng một công cụ JS hoàn toàn khác với các công cụ dành cho nhà phát triển Chrome và REPL của Node.js, cả hai đều sử dụng động cơ V8.

Đây là dòng ống cơ bản về những gì xảy ra khi bạn thực hiện lệnh JavaScript với Rhino trong trình bao Rhino.

  • Vỏ chạy org.mozilla.javascript.tools.shell.main.

  • Đổi lại, nó gọi đây là new IProxy(IProxy.EVAL_INLINE_SCRIPT); ví dụ, nếu mã được truyền trực tiếp với chuyển đổi nội tuyến -e.

  • Điều này đánh vào runphương pháp của IProxy .

  • Nó gọi evalInlineScript( src ). Điều này chỉ đơn giản là biên dịch chuỗi và đánh bại nó.

Về cơ bản:

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

Trong số ba, vỏ của Rhino là thứ gần giống với thực tế nhất evalmà không có sự bao bọc nào. Rhino's là gần nhất với một eval()tuyên bố thực tế và bạn có thể mong đợi nó hoạt động chính xác như evalsẽ.


1
(Không thực sự là một phần của câu trả lời, nhưng đáng nói là nodejs sử dụng mô-đun vm để trốn tránh theo mặc định khi sử dụng REPL và không chỉ là JavaScript eval)
Benjamin Gruenbaum

Bạn có thể giải thích tại sao tê giác , chẳng hạn, lại làm điều tương tự trong Terminal (không chỉ Chrome Console) không?
Ionică Bizău

5
+10 nếu có thể! Wow, bạn thực sự không có cuộc sống hoặc bạn thực sự thông minh hơn tôi để biết điều gì đó như thế. Xin vui lòng, cho tôi biết rằng bạn đã tìm kiếm một chút để tìm thấy câu trả lời này :)
Samuel

7
@Samuel Tất cả chỉ cần đọc nguồn - Tôi thề! Trong Chrome, nếu bạn nhập 'trình gỡ lỗi;' , bạn có được toàn bộ đường ống - nó sẽ ném bạn trực tiếp đến 'với' chỉ với một chức năng ở trên evaluateOn. Trong nút, mọi thứ đều được ghi lại rất tốt - họ có một mô-đun REPL chuyên dụng với tất cả lịch sử tốt đẹp và ấm cúng trên git, đã sử dụng REPL trước đây trên các chương trình của riêng tôi, tôi biết nơi để tìm :) Tôi rất vui vì bạn thích nó và tìm thấy nó hữu ích, nhưng tôi nợ nó vì sự quen thuộc của tôi với các cơ sở mã này (dev-tools và nodejs) hơn là trí tuệ của tôi. Đi thẳng vào nguồn thường luôn là dễ nhất.
Benjamin Gruenbaum

Cập nhật - API bảng điều khiển trong Chrome đã được cập nhật một chút vì vậy trong khi ý tưởng chung ở đây là chính xác, mã được đăng không chính xác cho phiên bản Chrome mới nhất. Xem chromium.googlesource.com/chromium/blink.git/+/master/Source/iêu
Benjamin Gruenbaum
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.