이것은 유효하며 "10"
JavaScript로 문자열 을 반환합니다 ( 더 많은 예제는 여기 ).
console.log(++[[]][+[]]+[+[]])
왜? 여기서 무슨 일이 일어나고 있습니까?
이것은 유효하며 "10"
JavaScript로 문자열 을 반환합니다 ( 더 많은 예제는 여기 ).
console.log(++[[]][+[]]+[+[]])
왜? 여기서 무슨 일이 일어나고 있습니까?
답변:
우리가 나누면 혼란은 다음과 같습니다.
++[[]][+[]]
+
[+[]]
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 프로덕션은 다음과 같이 평가됩니다.
expary는 UnaryExpression을 평가 한 결과입니다.
ToNumber (GetValue (expr))를 반환합니다.
ToNumber()
말한다 :
목적
다음 단계를 적용하십시오.
primValue를 ToPrimitive (입력 인수, 힌트 문자열)로 둡니다.
ToString (primValue)을 반환합니다.
ToPrimitive()
말한다 :
목적
Object의 기본값을 반환합니다. 개체의 기본값은 개체의 [[DefaultValue]] 내부 메서드를 호출하여 선택적 힌트 PreferredType을 전달하여 검색됩니다. [[DefaultValue]] 내부 메소드의 동작은 8.12.8의 모든 기본 ECMAScript 객체에 대해이 사양으로 정의됩니다.
[[DefaultValue]]
말한다 :
8.12.8 [[기본값]] (힌트)
힌트 문자열과 함께 [[DefaultValue]] 내부 메소드를 호출하면 다음 단계가 수행됩니다.
toString은 "toString"인수를 사용하여 객체 O의 [[Get]] 내부 메소드를 호출 한 결과입니다.
IsCallable (toString)이 true이면
ㅏ. str은 toString의 [[Call]] 내부 메소드를 호출 한 결과이며,이 값은 O이고 빈 인수 목록입니다.
비. str이 기본 값이면 str을 반환합니다.
.toString
배열의 말한다 :
15.4.4.2 Array.prototype.toString ()
toString 메소드가 호출되면 다음 단계가 수행됩니다.
이 값에 대해 ToObject를 호출 한 결과로 array를 사용합니다.
func는 인수 "join"을 가진 배열의 [[Get]] 내부 메소드를 호출 한 결과입니다.
IsCallable (func)이 false이면 func를 표준 내장 메소드 Object.prototype.toString (15.2.4.2)으로 설정하십시오.
배열을 제공하는 함수의 [[Call]] 내부 메소드를 this 값과 빈 인수 목록으로 호출 한 결과를 리턴하십시오.
그래서 +[]
내려 온다 +""
때문에 [].join() === ""
.
다시, +
는 다음과 같이 정의됩니다.
11.4.6 단항 + 연산자
단항 + 연산자는 피연산자를 숫자 유형으로 변환합니다.
UnaryExpression : + UnaryExpression 프로덕션은 다음과 같이 평가됩니다.
expary는 UnaryExpression을 평가 한 결과입니다.
ToNumber (GetValue (expr))를 반환합니다.
ToNumber
다음과 ""
같이 정의 됩니다.
StringNumericLiteral :::의 MV는 0입니다.
그래서 +"" === 0
, 이렇게 +[] === 0
.
true
, 값과 유형이 모두 같은 경우 에만 반환 합니다. 0 == ""
반환 true
(형 변환 후 동일),하지만 0 === ""
입니다 false
(안 같은 종류).
1 + [0]
끝나지 않습니다 . 참조 bclary.com/2004/11/07/#a-11.4.4"1" + [0]
++
++[[]][0]
실제로 반환 1
하지만 ++[]
오류가 발생합니다. 이것은 ++[[]][0]
끓는 것처럼 보이기 때문에 놀랍 습니다 ++[]
. 왜 ++[]
오류가 발생 ++[[]][0]
하지 않는지 아는 이유 가 있습니까?
PutValue
접두사 연산에서 문제가 전화 (ES3 용어, 8.7.2)에 있다고 확신합니다 . PutValue
반면 참조 요구 []
에 대한 참조를 생성하지 않습니다 그 자체로 표현 등을. 변수 참조 (이전에 정의한 var a = []
다음 ++a
작동) 가 포함 된 표현식 또는 객체의 속성 액세스 (예 [[]][0]
:)는 참조를 생성합니다. 간단히 말해 접두사 연산자는 값을 생성 할뿐만 아니라 해당 값을 넣을 곳도 필요합니다.
var a = []; ++a
, a
1입니다. 실행 후 ++[[]][0]
, [[]]
표현식으로 작성된 배열 은 이제 색인 0의 숫자 1 만 포함 ++
합니다.이를 수행하려면 참조가 필요합니다.
++[[]][+[]] => 1 // [+[]] = [0], ++0 = 1
[+[]] => [0]
그런 다음 문자열 연결이 있습니다
1+[0].toString() = 10
===
오히려 쓰는 것이 더 명확하지 =>
않습니까?
다음은 이 질문이 여전히 닫혀있는 동안 게시 한이 질문에 대답 하는 블로그 게시물 에서 수정 되었습니다. 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"
합니다.
마지막으로, 즉각적으로 명백하지 않을 수있는 것은 toString()
또는의 valueOf()
메소드 중 하나를 재정의 Array.prototype
하면 표현식의 결과가 변경된다는 것입니다. 오브젝트를 프리미티브 값으로 변환 할 때 둘 다 확인되고 사용되기 때문입니다. 예를 들면 다음과 같습니다.
Array.prototype.toString = function() {
return "foo";
};
++[[]][+[]]+[+[]]
... 생산합니다 "NaNfoo"
. 왜 이런 일이 독자를위한 연습으로 남게됩니까?
이것은 동일하지만 조금 작게 평가됩니다.
+!![]+''+(+[])
그래서 평가
+(true) + '' + (0)
1 + '' + 0
"10"
이제 당신은 그것을 가지고, 이것을 시도하십시오 :
_=$=+[],++_+''+$
"10"
숫자없이 "10"으로 식을 평가하는 가장 짧은 방법은 다음과 같습니다.
+!+[] + [+[]]
// "10"
-~[] + [+[]]
// "10"
// ========== 설명 ========== \\
+!+[]
: +[]
0으로 !0
변환합니다 true
. 로 변환합니다 . +true
1로 변환합니다.
-~[]
= -(-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]"
+ ''또는 + []는 0을 평가합니다.
++[[]][+[]]+[+[]] = 10
++[''][0] + [0] : First part is gives zeroth element of the array which is empty string
1+0
10
[]
입니다 하지 동일합니다 ""
. 먼저 요소가 추출 된 다음로 변환됩니다 ++
.
단계적으로 +
값을 숫자로 바꾸고 빈 배열에 추가하면 +[]
비어 있고 같으 0
므로
이제부터 코드를 살펴보면 ++[[]][+[]]+[+[]]
...
그리고 그들 사이에 플러스가 있습니다 ++[[]][+[]]
+[+[]]
그래서 이들은 [+[]]
반환 [0]
그들이 변환됩니다 하늘의 배열을 가지고 0
다른 배열 내부를 ...
그래서로서 상상 첫 번째 값 A는 2 차원 그럼 하나의 어레이 내부 배열과 [[]][+[]]
동일 할 것이다 [[]][0]
반환하는 []
...
그리고 마지막 ++
으로 그것을 변환하고 증가시킵니다 1
...
상상할 수 있듯이 1
+ "0"
는 "10"
...
+[]
에 하늘의 배열을 캐스팅0
) ... 다음 ... 오후를 낭비