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


136

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)

4
브라우저 콘솔 일뿐입니다. 콘솔에 로깅 시도 하면 NodeJS와 동일합니다. jsbin.com/oveyuj/1/edit
elclanrs

2
실제로 중복되지는 않지만 NodeJS 답변을 요청하고 있습니다. ... 다시에 대한 투표
Ionică Bizău

4
흠 ... 미안 그러나 stackoverflow.com/questions/9032856/...는 여전히 관련 및 상반기 응답
존 드보락

3
잊지 마세요 {}식 또는 원시 객체가 문맥에 따라 같은 하나 해석 될 수있다. 코드는 클라이언트와 서버에서 동일하지만 {}코드를 입력하는 컨텍스트가 다르기 때문에 다르게 해석 될 수 있습니다.
Patashu 2016 년

18
다시 다음 해주십시오 또 다시이 질문을 폐쇄 중단 이 질문은 정말이기 때문에 중복되지 .
Alvin Wong

답변:


132

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


1
(실제로 답변의 일부는 아니지만 nodejs를 언급 할 가치 는 JavaScript만이 아니라 REPL을 사용할 때 기본적으로 회피하기 위해 vm 모듈 을 사용합니다 eval)
Benjamin Gruenbaum

예를 들어 rhino 가 터미널에서 같은 일 을하는 이유를 설명 할 수 있습니까 (Chrome 콘솔뿐만 아니라)?
Ionică Bizău

5
가능하다면 +10! 와우 맨 .. 당신은 정말 인생이 없거나 그런 것보다 나보다 똑똑해 이 답변을 찾기 위해 조금만 검색했다고 알려주세요. :
Samuel

7
@Samuel 모든 소스를 읽는 것입니다-맹세합니다! Chrome에서 '디버거'를 입력하면 , 당신은 전체 파이프를 얻습니다- 위의 함수 하나만으로 'with'에 직접 던질 것 evaluateOn입니다. 노드에서 모든 것이 매우 잘 문서화되어 있습니다. 그들은 git에 대한 모든 역사가 멋지고 아늑한 전용 REPL 모듈을 가지고 있으며, 내 프로그램에서 REPL을 사용 했으므로 어디에서 볼 것인지 알고 있습니다.) 그것은 도움이되었지만 내 지성보다는이 코드베이스 (dev-tools 및 nodejs)에 익숙해졌습니다. 소스로 바로가는 것이 항상 가장 쉬운 경우가 많습니다.
Benjamin Gruenbaum

업데이트-Chrome의 콘솔 API가 약간 업데이트되었으므로 일반적인 아이디어는 정확하지만 게시 된 코드는 최신 버전의 Chrome에 정확하지 않습니다. 참조 chromium.googlesource.com/chromium/blink.git/+/master/Source/...
벤자민 Gruenbaum
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.