이것은 유효하며 "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
.
답변
++[[]][+[]] => 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"
. 왜 이런 일이 독자를위한 연습으로 남게됩니까?
답변
간단하게하자 :
++[[]][+[]]+[+[]] = "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"
답변
이것은 동일하지만 조금 작게 평가됩니다.
+!![]+''+(+[])
- []-배열을 더하거나 빼면 0으로 변환되어 변환되므로 + [] = 0
- ! []-false로 평가되므로 !! []는 true로 평가됩니다.
- + !! []-true를 true로 평가되는 숫자 값으로 변환하므로이 경우 1
- + ”-표현식에 빈 문자열을 추가하여 숫자를 문자열로 변환합니다.
- + []-0으로 평가
그래서 평가
+(true) + '' + (0)
1 + '' + 0
"10"
이제 당신은 그것을 가지고, 이것을 시도하십시오 :
_=$=+[],++_+''+$
답변
+ []는 0 […]으로 평가 한 다음 어떤 것으로 그것을 합산 (+ 연산)하여 배열 내용을 쉼표로 연결된 요소로 구성된 문자열 표현으로 변환합니다.
배열 인덱스를 가져 오는 것과 같은 다른 것 (+ 연산보다 우선 순위가 높음)은 서수이며 흥미로운 것은 아닙니다.
답변
숫자없이 “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]"