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


1657

이것은 유효하며 "10"JavaScript로 문자열 을 반환합니다 ( 더 많은 예제는 여기 ).

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

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


446
그 이해에 의해 시작 +[]에 하늘의 배열을 캐스팅 0) ... 다음 ... 오후를 낭비
deceze


10
wtfjs.com을 살펴보십시오 -explanatinos 와 같은 것들이 꽤 있습니다.
ThiefMaster

3
@deceze, 그런 종류의 것들을 어디서 배울 수 있습니까? 어떤 책? 저는 MDN에서 JS를 배우고 있으며 이런 것들을 가르치지 않습니다
Siddharth Thevaril

6
@SiddharthThevaril 당신이 방금 한 것과 같은 방법으로 누군가가 그것에 대해 게시하고 읽었습니다.
deceze

답변:


2070

우리가 나누면 혼란은 다음과 같습니다.

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

JavaScript에서는 사실입니다 +[] === 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". 하나의 요소로 배열을 결합하기 때문입니다. 결합하면로 구분 된 요소가 연결됩니다 ,. 하나의 요소를 사용하면이 논리가 첫 번째 요소 자체가된다고 추론 할 수 있습니다.

이 경우 +숫자와 배열의 두 피연산자가 나타납니다. 이제 두 가지를 같은 유형으로 강제하려고합니다. 먼저 배열이 string으로 강제 변환되고 그 "0"다음 숫자가 문자열 ( "1") 로 강제 변환됩니다 . 숫자 +문자열 ===문자열 .

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

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

이것은 미로이지만, 할 일은 +[]먼저 문자열로 변환되고 있기 때문입니다 +.

11.4.6 단항 + 연산자

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

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

  1. expary는 UnaryExpression을 평가 한 결과입니다.

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

ToNumber() 말한다 :

목적

다음 단계를 적용하십시오.

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

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

ToPrimitive() 말한다 :

목적

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

[[DefaultValue]] 말한다 :

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

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

  1. toString은 "toString"인수를 사용하여 객체 O의 [[Get]] 내부 메소드를 호출 한 결과입니다.

  2. IsCallable (toString)이 true이면

ㅏ. str은 toString의 [[Call]] 내부 메소드를 호출 한 결과이며,이 값은 O이고 빈 인수 목록입니다.

비. 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. 배열을 제공하는 함수의 [[Call]] 내부 메소드를 this 값과 빈 인수 목록으로 호출 한 결과를 리턴하십시오.

그래서 +[]내려 온다 +""때문에 [].join() === "".

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

11.4.6 단항 + 연산자

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

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

  1. expary는 UnaryExpression을 평가 한 결과입니다.

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

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

StringNumericLiteral :::의 MV는 0입니다.

그래서 +"" === 0, 이렇게 +[] === 0.


8
@ harper : 엄격한 평등 검사기입니다. 즉 true, 값과 유형이 모두 같은 경우 에만 반환 합니다. 0 == ""반환 true(형 변환 후 동일),하지만 0 === ""입니다 false(안 같은 종류).
pimvdb

41
이것의 일부가 올바르지 않습니다. 접두사 ( ) 연산자는 항상 숫자를 반환 하기 때문에 식은로 1 + [0]끝나지 않습니다 . 참조 bclary.com/2004/11/07/#a-11.4.4"1" + [0]++
팀 다운을

6
@Tim Down : 당신은 완전히 맞습니다. 나는 이것을 고치려고 노력하고 있지만 그렇게하려고 할 때 다른 것을 발견했다. 이것이 어떻게 가능한지 잘 모르겠습니다. ++[[]][0]실제로 반환 1하지만 ++[]오류가 발생합니다. 이것은 ++[[]][0]끓는 것처럼 보이기 때문에 놀랍 습니다 ++[]. 왜 ++[]오류가 발생 ++[[]][0]하지 않는지 아는 이유 가 있습니까?
pimvdb

11
@pimvdb : PutValue접두사 연산에서 문제가 전화 (ES3 용어, 8.7.2)에 있다고 확신합니다 . PutValue반면 참조 요구 []에 대한 참조를 생성하지 않습니다 그 자체로 표현 등을. 변수 참조 (이전에 정의한 var a = []다음 ++a작동) 가 포함 된 표현식 또는 객체의 속성 액세스 (예 [[]][0]:)는 참조를 생성합니다. 간단히 말해 접두사 연산자는 값을 생성 할뿐만 아니라 해당 값을 넣을 곳도 필요합니다.
Tim Down

13
@pimvdb : 실행 후 var a = []; ++a, a1입니다. 실행 후 ++[[]][0], [[]]표현식으로 작성된 배열 은 이제 색인 0의 숫자 1 만 포함 ++합니다.이를 수행하려면 참조가 필요합니다.
Tim Down

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

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

1+[0].toString() = 10

7
===오히려 쓰는 것이 더 명확하지 =>않습니까?
Mateen Ulhaq

61

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

첫째, 의견 : 이런 종류의 표현은 (정확한) 프로덕션 환경에서 절대로 나타나지 않을 것이며 독자가 JavaScript의 더티 에지를 얼마나 잘 알고 있는지에 대한 연습으로 만 사용됩니다. JavaScript 연산자가 형식간에 암시 적으로 변환하는 일반적인 원칙은 일부 일반적인 변환과 마찬가지로 유용하지만이 경우의 세부 사항은 그다지 유용하지 않습니다.

표현 ++[[]][+[]]+[+[]]은 처음에는 다소 강렬하고 모호한 것처럼 보일 수 있지만 실제로는 별도의 표현으로 비교적 쉽게 분류 할 수 있습니다. 아래에는 명확성을 위해 괄호를 추가했습니다. 나는 그들이 아무것도 바꾸지 않을 것을 확신 할 수 있지만, 그것을 확인하고 싶다면 그룹화 연산자 에 대해 자유롭게 읽으십시오 . 따라서 표현을보다 명확하게 작성할 수 있습니다.

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

이를 분석하면 다음으로 +[]평가 되는 것을 관찰하여 단순화 할 수 있습니다 0. 왜 이것이 사실인지 스스로 만족시키기 위해 단항 + 연산자를 확인 하고 약간 비참한 흔적을 따라 가면 ToPrimitive 가 빈 배열을 빈 문자열로 변환 한 다음 ToNumber0의해 변환됩니다 . 이제 각 인스턴스를 대체 할 수 있습니다 .0+[]

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

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

그래서, 우리는 간단하게 할 수 [[]][0]만에 []우리는이 ++[], 오른쪽? 실제로, 평가 ++[]하면 오류가 발생하여 처음에는 혼란스러워 보일 수 있기 때문에 그렇지 않습니다 . 그러나 특성의 특성에 대해 조금만 생각 ++하면 명확 해집니다. 변수 (예 :) ++i또는 객체 속성 (예 :)을 증가시키는 데 사용됩니다 ++obj.count. 값으로 평가 될뿐만 아니라 해당 값을 어딘가에 저장합니다. 의 경우 ++[]업데이트 할 객체 속성 또는 변수에 대한 참조가 없으므로 새 값을 입력 할 위치가 없습니다. 스펙 측면에서 이는 내부 PutValue 오퍼레이션에 의해 다루어지며 두부 증가 연산자에 의해 호출됩니다.

그렇다면 어떻게 ++[[]][0]해야합니까? 와 비슷한 논리에 의해 +[]내부 배열이 변환 0되고이 값이 증가 1하여 최종 값을 제공 1합니다. 0외부 배열 의 속성 값 이로 업데이트되고 1전체식이로 평가됩니다 1.

이것은 우리를 떠나

1 + [0]

... 더하기 연산자를 간단하게 사용합니다 . 두 피연산자는 먼저 프리미티브로 변환되고 프리미티브 값이 문자열이면 문자열 연결이 수행되고 그렇지 않으면 숫자 추가가 수행됩니다. [0]로 변환 "0"하므로 문자열 연결이 사용되어을 생성 "10"합니다.

마지막으로, 즉각적으로 명백하지 않을 수있는 것은 toString()또는의 valueOf()메소드 중 하나를 재정의 Array.prototype하면 표현식의 결과가 변경된다는 것입니다. 오브젝트를 프리미티브 값으로 변환 할 때 둘 다 확인되고 사용되기 때문입니다. 예를 들면 다음과 같습니다.

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

... 생산합니다 "NaNfoo". 왜 이런 일이 독자를위한 연습으로 남게됩니까?


24

간단하게하자 :

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

13

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

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

그래서 평가

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

이제 당신은 그것을 가지고, 이것을 시도하십시오 :

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

아직 "10"으로 평가되지 않습니다. 그러나 이것은 다른 방식으로하고 있습니다. 크롬과 같은 자바 스크립트 관리자에서 이것을 평가하십시오.
Vlad Shlosberg

_ = $ = + [], ++ _ + ''+ $-> _ = $ = 0, ++ _ + ''+ $-> _ = 0, $ = 0, ++ _ + ''+ $ -> ++ 0 + ''+ 0-> 1 + ''+ 0-> '10'// Yei : v
LeagueOfJava

이것은 동일한 것으로 평가되지만 당신보다 훨씬 작습니다."10"
ADJenks

7

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

배열 인덱스를 가져 오는 것과 같은 다른 것 (+ 연산보다 우선 순위가 높음)은 서수이며 흥미로운 것은 아닙니다.


4

숫자없이 "10"으로 식을 평가하는 가장 짧은 방법은 다음과 같습니다.

+!+[] + [+[]] // "10"

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

// ========== 설명 ========== \\

+!+[]: +[]0으로 !0변환합니다 true. 로 변환합니다 . +true1로 변환합니다. -~[]= -(-1)1

[+[]]: +[]0 [0]으로 변환합니다 . 단일 요소가 0 인 배열입니다.

이어서 JS는 평가 1 + [0], 따라서 Number + Array표현. 그런 다음 ECMA 사양이 작동합니다. +연산자는 toString()/valueOf()기본 Object프로토 타입 에서 함수를 호출하여 두 피연산자를 문자열로 변환합니다 . 표현식의 두 피연산자가 모두 숫자 인 경우 덧셈 함수로 작동합니다. 요령은 배열이 요소를 연결된 문자열 표현으로 쉽게 변환한다는 것입니다.

몇 가지 예 :

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

두 가지 Objects추가로 인한 예외는 다음과 NaN같습니다.

[] + []   //    ""
[1] + [2] //    "12"
{} + {}   //    NaN
{a:1} + {b:2}     //    NaN
[1, {}] + [2, {}] //    "1,[object Object]2,[object Object]"

1
  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
    

1
대답은 혼란스럽고 혼란 스럽습니다. []입니다 하지 동일합니다 "". 먼저 요소가 추출 된 다음로 변환됩니다 ++.
PointedEars

1

단계적으로 +값을 숫자로 바꾸고 빈 배열에 추가하면 +[]비어 있고 같으 0므로

이제부터 코드를 살펴보면 ++[[]][+[]]+[+[]]...

그리고 그들 사이에 플러스가 있습니다 ++[[]][+[]]+[+[]]

그래서 이들은 [+[]]반환 [0]그들이 변환됩니다 하늘의 배열을 가지고 0다른 배열 내부를 ...

그래서로서 상상 첫 번째 값 A는 2 차원 그럼 하나의 어레이 내부 배열과 [[]][+[]]동일 할 것이다 [[]][0]반환하는 []...

그리고 마지막 ++으로 그것을 변환하고 증가시킵니다 1...

상상할 수 있듯이 1+ "0""10"...

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.