이것은 유효하며 JavaScript에서 "10"
을 반환합니다(여기에 더 많은 예가 있음 ).
console.log(++[[]][+[]]+[+[]])
왜요? 여기에서 무슨 일이 일어나고 있습니까?
질문자 :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은 다음과 같이 평가됩니다.
expr을 UnaryExpression을 평가한 결과라고 합시다.
ToNumber(GetValue(expr))를 반환합니다.
ToNumber()
말한다:
물체
다음 단계를 적용합니다.
primValue를 ToPrimitive(입력 인수, 힌트 문자열)로 설정합니다.
ToString(primValue)을 반환합니다.
ToPrimitive()
는 다음과 같이 말합니다.
물체
개체의 기본값을 반환합니다. 객체의 기본값은 객체의 [[DefaultValue]] 내부 메서드를 호출하고 선택적 힌트 PreferredType을 전달하여 검색됩니다. [[DefaultValue]] 내부 메서드의 동작은 8.12.8의 모든 기본 ECMAScript 개체에 대해 이 사양에 의해 정의됩니다.
[[DefaultValue]]
은 다음과 같이 말합니다.
8.12.8 [[기본값]] (힌트)
힌트 문자열로 O의 [[DefaultValue]] 내부 메소드를 호출하면 다음 단계가 수행됩니다.
toString을 인수 "toString"으로 개체 O의 [[Get]] 내부 메서드를 호출한 결과라고 합니다.
IsCallable(toString)이 참이면,
NS. str을 toString의 [[Call]] 내부 메서드를 호출한 결과라고 하고 O를 this 값으로 하고 빈 인수 목록을 사용합니다.
NS. 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)으로 설정합니다.
배열을 this 값으로 제공하고 빈 인수 목록을 제공하는 func의 [[Call]] 내부 메서드를 호출한 결과를 반환합니다.
따라서 +[]
는 [].join() === ""
이기 때문에 +""
"" 가 됩니다.
다시 +
는 다음과 같이 정의됩니다.
11.4.6 단항 + 연산자
단항 + 연산자는 피연산자를 숫자 유형으로 변환합니다.
프로덕션 UnaryExpression : + UnaryExpression은 다음과 같이 평가됩니다.
expr을 UnaryExpression을 평가한 결과라고 합시다.
ToNumber(GetValue(expr))를 반환합니다.
ToNumber
는 ""
대해 다음과 같이 정의됩니다.
StringNumericLiteral ::: [empty]의 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"
생성합니다.
마지막으로, 즉시 명확하지 않을 수 있는 것은 Array.prototype
toString()
또는 valueOf()
메서드 중 하나를 재정의하면 표현식의 결과가 변경된다는 것입니다. 객체를 원시 값으로 변환합니다. 예를 들어, 다음
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"
이것은 동일하지만 조금 더 작은 것으로 평가됩니다.
+!![]+''+(+[])
그래서 평가
+(true) + '' + (0) 1 + '' + 0 "10"
이제 당신은 그것을 얻었습니다. 이것을 시도하십시오.
_=$=+[],++_+''+$
+[]는 0 [...]으로 평가된 다음 아무 것과 합산(+ 연산)하여 배열 내용을 쉼표로 결합된 요소로 구성된 문자열 표현으로 변환합니다.
배열의 인덱스를 가져오는 것과 같은 다른 것(+ 연산보다 우선 순위가 높음)은 순서이며 흥미롭지 않습니다.
단계별로 +
값을 숫자로 바꾸고 빈 배열에 추가하면 +[]
... 비어 있고 0
과 같기 때문에
이제 거기에서 코드를 살펴보십시오. ++[[]][+[]]+[+[]]
...
그리고 그들 사이에 더하기가 있습니다 ++[[]][+[]]
+ [+[]]
따라서 이러한 [+[]]
는 다른 배열 내부에서 0
변환되는 빈 배열이 있으므로 [0]
따라서 상상할 수 있듯이 첫 번째 값은 내부에 하나의 배열 이 있는 2차원 [[]][+[]]
[[]][0]
과 같 []
...를 반환합니다.
그리고 마지막에 ++
변환하고 1
늘리십시오 ...
1
+ "0"
"10"
이 될 것이라고 상상할 수 있습니다.
"10"
으로 평가하는 가장 짧은 방법은 다음과 같습니다.
+!+[] + [+[]] // "10" -~[] + [+[]] // "10"
+!+[]
:+[]
0
으로 평가됩니다.!0
true
로 평가됩니다.+true
1
로 평가됩니다.-~[]
와 동일하다 -(-1)
로 평가되는 1
.[+[]]
:+[]
는 0으로 평가됩니다.[0]
은 단일 요소가 0
배열입니다. 그런 다음 JS는 1 + [0]
, 숫자 + 배열 표현식을 평가합니다. 그러면 ECMA 사양이 작동합니다. +
연산자는 ToPrimitive 및 ToString 추상 작업을 호출하여 두 피연산자를 문자열로 변환합니다. 표현식의 두 피연산자가 모두 숫자인 경우 덧셈 함수로 작동합니다. 트릭은 배열이 요소를 연결된 문자열 표현으로 쉽게 강제 변환한다는 것입니다.
몇 가지 예:
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
+'' 또는 +[]는 0을 평가합니다.
++[[]][+[]]+[+[]] = 10 ++[''][0] + [0] : First part is gives zeroth element of the array which is empty string 1+0 10
++[[]][+[]]+[+[]] ^^^ | 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
으로 강제 변환하는 것과 같습니다.
피연산자가 문자열이면 +
연결됩니다.
출처 : http:www.stackoverflow.com/questions/7202157/why-does-return-the-string-10
임의의 문자열을 사용하는 이 코드가 "hello world"를 인쇄하는 이유는 무엇입니까? (0) | 2022.01.04 |
---|---|
두 가지 다른 지점의 파일을 비교하는 방법 (0) | 2022.01.02 |
문자열이 숫자(float)인지 어떻게 확인합니까? (0) | 2022.01.02 |
JavaScript 객체 배열에서 id로 객체 찾기 (0) | 2022.01.02 |
Java에서 문자열을 분할하는 방법 (0) | 2022.01.02 |