etc./StackOverFlow

++[[]][+[]]+[+[]]가 문자열 "10"을 반환하는 이유는 무엇입니까?

청렴결백한 만능 재주꾼 2022. 1. 2. 04:54
반응형

질문자 :Community Wiki


이것은 유효하며 JavaScript에서 "10" 을 반환합니다(여기에 더 많은 예가 있음 ).

 console.log(++[[]][+[]]+[+[]])

왜요? 여기에서 무슨 일이 일어나고 있습니까?



그것을 나누면 혼란은 다음과 같습니다.

 ++[[]][+[]] + [+[]]

+[] === 0 것이 사실입니다. + 는 무언가를 숫자로 변환하며, 이 경우 +"" 또는 0 (아래 세부 사양 참조).

따라서 우리는 그것을 단순화할 수 있습니다( ++ + 보다 우선합니다):

 ++[[]][0] + [0]

[[]][0] [[]] 에서 첫 번째 요소 가져오기, 다음이 사실입니다.

[[]][0] 은 내부 배열( [] )을 반환합니다. [[]][0] === [] 라고 말하는 것이 잘못되었지만 잘못된 표기법을 피하기 위해 내부 배열 A

++ 는 "1씩 증가하고 증가된 결과를 반환"을 의미합니다. 따라서 ++[[]][0]Number(A) + 1 (또는 +A + 1 )과 같습니다.

다시 말하지만, 혼란을 더 읽기 쉬운 것으로 단순화할 수 있습니다. A [] 로 바꿔보자.

 (+[] + 1) + [0]

+[] 가 배열을 숫자 0 으로 강제 변환하려면 먼저 문자열로 강제 변환해야 합니다. 다시 "" 마지막으로 1 이 추가되어 1 됩니다.

  • (+[] + 1) === (+"" + 1)
  • (+"" + 1) === (0 + 1)
  • (0 + 1) === 1

더 단순화해 보겠습니다.

 1 + [0]

또한 JavaScript: [0] == "0" 에서도 마찬가지입니다. 하나의 요소가 있는 배열을 결합하기 때문입니다. , 구분된 요소가 연결됩니다. 하나의 요소를 사용하면 이 논리가 첫 번째 요소 자체가 될 것이라고 추론할 수 있습니다.

이 경우 + 는 두 개의 피연산자, 즉 숫자와 배열을 봅니다. 이제 둘을 같은 유형으로 강제하려고 합니다. 먼저 배열이 문자열 "0" 으로 강제 변환되고 다음으로 숫자가 문자열( "1" )로 강제 변환됩니다. 숫자 + 문자열 === 문자열 .

 "1" + "0" === "10" // Yay!

+[] 대한 사양 세부정보:

이것은 꽤 미로이지만 +[] 를 수행하려면 먼저 문자열로 변환됩니다. 왜냐하면 + 가 다음과 같이 말하기 때문입니다.

11.4.6 단항 + 연산자

단항 + 연산자는 피연산자를 숫자 유형으로 변환합니다.

프로덕션 UnaryExpression : + UnaryExpression은 다음과 같이 평가됩니다.

  1. expr을 UnaryExpression을 평가한 결과라고 합시다.

  2. ToNumber(GetValue(expr))를 반환합니다.

ToNumber() 말한다:

물체

다음 단계를 적용합니다.

  1. primValue를 ToPrimitive(입력 인수, 힌트 문자열)로 설정합니다.

  2. ToString(primValue)을 반환합니다.

ToPrimitive() 는 다음과 같이 말합니다.

물체

개체의 기본값을 반환합니다. 객체의 기본값은 객체의 [[DefaultValue]] 내부 메서드를 호출하고 선택적 힌트 PreferredType을 전달하여 검색됩니다. [[DefaultValue]] 내부 메서드의 동작은 8.12.8의 모든 기본 ECMAScript 개체에 대해 이 사양에 의해 정의됩니다.

[[DefaultValue]] 은 다음과 같이 말합니다.

8.12.8 [[기본값]] (힌트)

힌트 문자열로 O의 [[DefaultValue]] 내부 메소드를 호출하면 다음 단계가 수행됩니다.

  1. toString을 인수 "toString"으로 개체 O의 [[Get]] 내부 메서드를 호출한 결과라고 합니다.

  2. IsCallable(toString)이 참이면,

NS. str을 toString의 [[Call]] 내부 메서드를 호출한 결과라고 하고 O를 this 값으로 하고 빈 인수 목록을 사용합니다.

NS. str이 기본 값이면 str을 반환합니다.

배열의 .toString 은 다음과 같이 말합니다.

15.4.4.2 Array.prototype.toString( )

toString 메서드가 호출되면 다음 단계가 수행됩니다.

  1. 이 값에 대해 ToObject를 호출한 결과를 array라고 합니다.

  2. func를 인수 "join"으로 배열의 [[Get]] 내부 메서드를 호출한 결과라고 합니다.

  3. IsCallable(func)이 false이면 func를 표준 내장 메서드 Object.prototype.toString(15.2.4.2)으로 설정합니다.

  4. 배열을 this 값으로 제공하고 빈 인수 목록을 제공하는 func의 [[Call]] 내부 메서드를 호출한 결과를 반환합니다.

따라서 +[][].join() === "" 이기 때문에 +"" "" 가 됩니다.

다시 + 는 다음과 같이 정의됩니다.

11.4.6 단항 + 연산자

단항 + 연산자는 피연산자를 숫자 유형으로 변환합니다.

프로덕션 UnaryExpression : + UnaryExpression은 다음과 같이 평가됩니다.

  1. expr을 UnaryExpression을 평가한 결과라고 합시다.

  2. ToNumber(GetValue(expr))를 반환합니다.

ToNumber"" 대해 다음과 같이 정의됩니다.

StringNumericLiteral ::: [empty]의 MV는 0입니다.

따라서 +"" === 0 , 따라서 +[] === 0 입니다.


pimvdb

++[[]][+[]] => 1 // [+[]] = [0], ++0 = 1 [+[]] => [0]

그런 다음 문자열 연결이 있습니다.

 1+[0].toString() = 10

Shef

다음은 이 질문이 아직 닫혀 있는 동안 게시한 이 질문에 답변 하는 블로그 게시물 에서 수정한 것입니다. 링크는 ECMAScript 3 사양(의 HTML 사본)으로, 오늘날 일반적으로 사용되는 웹 브라우저에서 여전히 JavaScript의 기준입니다.

첫째, 주석: 이러한 종류의 표현은 (제정신) 생산 환경에서 절대 나타나지 않을 것이며 독자가 JavaScript의 더러운 가장자리를 얼마나 잘 알고 있는지에 대한 연습으로만 사용됩니다. JavaScript 연산자가 유형 간에 암시적으로 변환하는 일반적인 원칙은 일부 일반적인 변환과 마찬가지로 유용하지만 이 경우의 세부 사항 중 많은 부분은 그렇지 않습니다.

++[[]][+[]]+[+[]] 표현식은 처음에는 다소 위풍당당하고 모호해 보일 수 있지만 실제로는 별도의 표현식으로 분해하기가 비교적 쉽습니다. 아래에는 명확성을 위해 단순히 괄호를 추가했습니다. 나는 그들이 아무것도 변경하지 않는다고 확신할 수 있지만, 만약 당신이 그것을 확인하고 싶다면 자유롭게 그룹화 연산자 에 대해 읽어보십시오. 따라서 표현은 다음과 같이 더 명확하게 쓸 수 있습니다.

 ( ++[[]][+[]] ) + ( [+[]] )

+[]0 평가되는 것을 관찰하여 단순화할 수 있습니다. 이것이 사실인 이유를 스스로 만족시키려면 단항 + 연산자를 확인하고 ToPrimitive 가 빈 배열을 빈 문자열로 변환하는 것으로 끝나는 약간 구불구불한 흔적을 따르십시오. 그런 다음 ToNumber에 의해 0 으로 변환됩니다. +[] 의 각 인스턴스에 대해 0 을 대체할 수 있습니다.

 ( ++[[]][0] ) + [0]

이미 더 간단합니다. ++[[]][0] , 이는 접두사 증가 연산자 ( ++ ), 그 자체가 빈 배열인 단일 요소( [[]] ) 및 속성 접근 자로 배열을 정의하는 배열 리터럴의 조합입니다. ( [0] ) 배열 리터럴에 의해 정의된 배열에서 호출됩니다.

그래서, 우리는 간단하게 할 수 [[]][0] 그냥 [] 우리가 가지고 ++[] , 맞죠? ++[] 평가하면 처음에는 혼란스러워 보일 수 있는 오류가 발생하기 때문에 그렇지 않습니다. ++ 의 특성에 대해 조금만 생각해 ++i ) 또는 개체 속성(예: ++obj.count )을 증가시키는 데 사용됩니다. 값을 평가할 뿐만 아니라 해당 값을 어딘가에 저장합니다. ++[] 의 경우 업데이트할 개체 속성이나 변수에 대한 참조가 없기 때문에 새 값(무엇이든 간에)을 넣을 곳이 없습니다. 사양 측면에서 이것은 접두사 증가 연산자에 의해 호출되는 내부 PutValue 작업으로 처리됩니다.

그렇다면 ++[[]][0] 은 무엇을 합니까? 그런데, 유사한 로직 +[] 내부 배열로 변환되어 0 이 값만큼 증가된다 1 우리의 최종 값을 제공하기 위해 1 . 외부 배열의 속성 0 1 로 업데이트되고 전체 표현식은 1 평가됩니다.

이것은 우리에게

 1 + [0]

... 덧셈 연산자 의 간단한 사용입니다. 두 피연산자는 모두 기본적으로 먼저 변환되며 기본 값 중 하나가 문자열이면 문자열 연결이 수행되고 그렇지 않으면 숫자 추가가 수행됩니다. [0]"0" 변환되므로 문자열 연결이 사용되어 "10" 생성합니다.

마지막으로, 즉시 명확하지 않을 수 있는 것은 Array.prototype toString() 또는 valueOf() 메서드 중 하나를 재정의하면 표현식의 결과가 변경된다는 것입니다. 객체를 원시 값으로 변환합니다. 예를 들어, 다음

 Array.prototype.toString = function() { return "foo"; }; ++[[]][+[]]+[+[]]

"NaNfoo" 생성합니다. 왜 이런 일이 일어나는지는 독자를 위한 연습으로 남겨둡니다...


Tim Down

간단하게 만들어 보겠습니다.

 ++[[]][+[]]+[+[]] = "10" var a = [[]][+[]]; var b = [+[]]; // so a == [] and b == [0] ++a; // then a == 1 and b is still that array [0] // when you sum the var a and an array, it will sum b as a string just like that: 1 + "0" = "10"

renatoluna

이것은 동일하지만 조금 더 작은 것으로 평가됩니다.

 +!![]+''+(+[])
  • [] - 배열이 변환되어 더하거나 빼면 0으로 변환되므로 +[] = 0
  • ![] - 거짓으로 평가되므로 !![]는 참으로 평가됩니다.
  • +!![] - true를 true로 평가되는 숫자 값으로 변환하므로 이 경우 1
  • +'' - 표현식에 빈 문자열을 추가하여 숫자가 문자열로 변환되도록 합니다.
  • +[] - 0으로 평가

그래서 평가

 +(true) + '' + (0) 1 + '' + 0 "10"

이제 당신은 그것을 얻었습니다. 이것을 시도하십시오.

 _=$=+[],++_+''+$

Vlad Shlosberg

+[]는 0 [...]으로 평가된 다음 아무 것과 합산(+ 연산)하여 배열 내용을 쉼표로 결합된 요소로 구성된 문자열 표현으로 변환합니다.

배열의 인덱스를 가져오는 것과 같은 다른 것(+ 연산보다 우선 순위가 높음)은 순서이며 흥미롭지 않습니다.


Eskat0n

단계별로 + 값을 숫자로 바꾸고 빈 배열에 추가하면 +[] ... 비어 있고 0 과 같기 때문에

이제 거기에서 코드를 살펴보십시오. ++[[]][+[]]+[+[]] ...

그리고 그들 사이에 더하기가 있습니다 ++[[]][+[]] + [+[]]

따라서 이러한 [+[]] 는 다른 배열 내부에서 0 변환되는 빈 배열이 있으므로 [0]

따라서 상상할 수 있듯이 첫 번째 값은 내부에 하나의 배열 이 있는 2차원 [[]][+[]] [[]][0] 과 같 [] ...를 반환합니다.

그리고 마지막에 ++ 변환하고 1 늘리십시오 ...

1 + "0" "10" 이 될 것이라고 상상할 수 있습니다.

문자열 "10"을 반환하는 이유는 무엇입니까?


Community Wiki

"10" 으로 평가하는 가장 짧은 방법은 다음과 같습니다.

 +!+[] + [+[]] // "10" -~[] + [+[]] // "10"

설명

  • +!+[] :
    • +[] 0 으로 평가됩니다.
    • !0 true 로 평가됩니다.
    • +true 1 로 평가됩니다.
  • -~[] 와 동일하다 -(-1) 로 평가되는 1 .
  • [+[]] :
    • +[] 는 0으로 평가됩니다.
    • [0] 은 단일 요소가 0 배열입니다.

그런 다음 JS는 1 + [0] , 숫자 + 배열 표현식을 평가합니다. 그러면 ECMA 사양이 작동합니다. + 연산자는 ToPrimitiveToString 추상 작업을 호출하여 두 피연산자를 문자열로 변환합니다. 표현식의 두 피연산자가 모두 숫자인 경우 덧셈 함수로 작동합니다. 트릭은 배열이 요소를 연결된 문자열 표현으로 쉽게 강제 변환한다는 것입니다.

몇 가지 예:

 1 + {} // "1[object Object]" 1 + [] // "1" 1 + new Date() // "1Wed Jun 19 2013 12:13:25 GMT+0400 (Caucasus Standard Time)" [] + [] // "" [1] + [2] // "12" {} + {} // "[object Object][object Object]" ¹ {a:1} + {b:2} // "[object Object][object Object]" ¹ [1, {}] + [2, {}] // "1,[object Object]2,[object Object]"

¹: 각 행은 표현식 컨텍스트에서 평가됩니다. 첫 번째 {} 는 명령문 컨텍스트의 경우처럼 블록이 아니라 객체 리터럴입니다. REPL에서는 대부분의 REPL이 명령문 컨텍스트에서 작동하기 때문에 {} + {}NaN 여기서 첫 번째 {}블록 이고 코드는 {}; +{}; + 가 개체를 숫자로 강제 변환하기 때문에 최종 표현식 문(그 값이 완료 레코드의 결과가 됨)은 NaN


Community Wiki

  1. 단항 플러스 주어진 문자열은 숫자로 변환
  2. 문자열이 주어진 증분 연산자는 변환하고 1씩 증가시킵니다.
  3. [] == ''. 빈 문자열
  4. +'' 또는 +[]는 0을 평가합니다.

     ++[[]][+[]]+[+[]] = 10 ++[''][0] + [0] : First part is gives zeroth element of the array which is empty string 1+0 10

Praveen Vedanth

++[[]][+[]]+[+[]] ^^^ | v ++[[]][+[]]+[0] ^^^ | v ++[[]][0]+[0] ^^^^^^^ | v ++[]+[0] ^^^ | v ++[]+"0" ^^^^ | v ++0+"0" ^^^ | v 1+"0" ^^^^^ | v "10"

+ .valueOf() 를 통해 숫자가 아닌 피연산자를 강제 변환합니다. 숫자를 반환하지 않으면 .toString() 이 호출됩니다.

다음과 같이 간단히 확인할 수 있습니다.

 const x = [], y = []; x.valueOf = () => (console.log('x.valueOf() has been called'), y.valueOf()); x.toString = () => (console.log('x.toString() has been called'), y.toString()); console.log(`+x -> ${+x}`);

따라서 +[] "" 를 숫자 0 으로 강제 변환하는 것과 같습니다.

피연산자가 문자열이면 + 연결됩니다.


Community Wiki

출처 : http:www.stackoverflow.com/questions/7202157/why-does-return-the-string-10

반응형