[javascript] 왜 {} + {}가 클라이언트 측에서만 NaN입니까? Node.js에없는 이유는 무엇입니까?

While [] + []은 빈 문자열이며 [] + {}is "[object Object]"이며 {} + []is 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)



답변

업데이트 참고 : 이 크롬 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 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;
}

따라서 기본적으로 call표현식으로 객체를 수행합니다 . 표현은 다음과 같습니다.

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

보시다시피, 랩핑 괄호없이 표현식이 직접 평가됩니다.

Node.js가 다르게 작동하는 이유

Node.js의 소스는 이것을 정당화합니다.

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

노드는 항상 이런 식으로 행동하지 않았습니다. 변경 한 실제 커밋 은 다음과 같습니다 . Ryan은이 변경에 대해 다음과 같은 의견을 남겼습니다. 차이점의 예를 들어 “REPL 명령의 회피 방법 개선”.


코뿔소

업데이트 -OP는 Rhino의 동작 방식 과 nodejs와 달리 Chrome devtools와 같은 방식에 관심이있었습니다 .

Rhino는 Chrome 개발자 도구 및 V8을 사용하는 Node.js의 REPL과 달리 완전히 다른 JS 엔진을 사용합니다.

다음은 Rhino 셸에서 Rhino와 JavaScript 명령을 평가할 때 발생하는 기본 파이프 라인입니다.

  • 쉘이 실행됩니다 org.mozilla.javascript.tools.shell.main.

  • 결과적으로 코드가 인라인 스위치 -e로 직접 전달 된 경우 이를 호출 합니다 new IProxy(IProxy.EVAL_INLINE_SCRIPT); .

  • 이것은 IProxy의 run방법에 맞습니다 .

  • evalInlineScript( src )를 호출합니다 . 이것은 단순히 문자열을 컴파일하고 회피합니다.

원래:

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

세 가지 중에서 Rhino의 쉘은 eval랩핑없이 실제와 가장 가까운 것을 수행합니다 . Rhino ‘s는 실제 eval()진술과 가장 유사하며, 당신은 그것이 정확하게 행동하는 것을 기대할 수 있습니다 eval.


답변