재미있고 까다로운 JavaScript 예제 목록
자바스크립트는 훌륭한 언어입니다. 간단한 구문, 대규모 생태계, 그리고 가장 중요한 것은 훌륭한 커뮤니티를 갖추고 있습니다.
동시에, 우리 모두는 JavaScript가 까다로운 부분이 있는 꽤 재미있는 언어라는 것을 알고 있습니다. 그들 중 일부는 우리의 일상 업무를 순식간에 지옥으로 만들 수도 있고, 일부는 우리를 크게 웃게 만들 수도 있습니다.
WTFJS의 원래 아이디어는 Brian Leroux의 것입니다. 이 목록은 dotJS 2012에서 열린 그의 강연 "WTFJS" 에서 많은 영감을 받았습니다.
npm
사용하여 이 핸드북을 설치할 수 있습니다. 그냥 실행하세요:
$ npm install -g wtfjs
이제 명령줄에서 wtfjs
실행할 수 있습니다. 선택한 $PAGER
에서 매뉴얼이 열립니다. 그렇지 않은 경우 여기에서 계속 읽으셔도 됩니다.
소스는 여기에서 확인할 수 있습니다: https://github.com/denysdovhan/wtfjs
현재 wtfjs 의 번역은 다음과 같습니다.
귀하의 언어로 번역하는 데 도움을 주세요
참고: 번역은 번역가가 유지 관리합니다. 여기에는 모든 예시가 포함되어 있지 않을 수도 있고 기존 예시가 오래되었을 수도 있습니다.
[]
같습니다 ![]
true
![]
와 같지 않지만 []
와도 같지 않습니다.NaN
NaN
이 아니다Object.is()
및 ===
이상한 경우[]
사실이지만 true
아닙니다.null
은 거짓이지만 false
아닙니다.document.all
은 객체이지만 정의되지 않았습니다.undefined
및 Number
parseInt
나쁜 놈이다true
과 false
있는 수학NaN
은[]
및 null
은 객체입니다.0.1 + 0.2
String
의 인스턴스가 아닙니다.constructor
속성__proto__
사용하여 프로토타입에 액세스`${{Object}}`
try..catch
arguments
및 화살표 함수Number.toFixed()
다른 숫자를 표시합니다.Math.max()
Math.min()
보다 작습니다.null
0
과 비교{}{}
(는) 정의되지 않았습니다.arguments
바인딩alert
setTimeout
객체true
의 비엄격한 비교그냥 재미로
— “재미를 위해: 우연한 혁명가의 이야기” , 리누스 토발즈
이 목록의 주요 목표는 몇 가지 이상한 예제를 수집하고 가능하다면 작동 방식을 설명하는 것입니다. 단지 우리가 이전에 몰랐던 것을 배우는 것이 재미있기 때문입니다.
초보자라면 이 노트를 사용하여 JavaScript에 대해 더 깊이 알아볼 수 있습니다. 이 노트가 여러분이 사양을 읽는 데 더 많은 시간을 할애하도록 동기를 부여하기를 바랍니다.
전문 개발자라면 이 예제를 우리가 사랑하는 JavaScript의 모든 특이점과 예상치 못한 장점에 대한 훌륭한 참고 자료로 간주할 수 있습니다.
어쨌든 이 글을 읽어보세요. 아마도 새로운 것을 발견하게 될 것입니다.
️ 참고: 이 문서를 재미있게 읽으셨다면 이 컬렉션의 작성자를 후원해 보시기 바랍니다.
// ->
표현식의 결과를 표시하는 데 사용됩니다. 예를 들어:
1 + 1 ; // -> 2
// >
console.log
또는 다른 출력의 결과를 의미합니다. 예를 들어:
console . log ( "hello, world!" ) ; // > hello, world!
//
설명을 위해 사용된 주석입니다. 예:
// Assigning a function to foo constant
const foo = function ( ) { } ;
[]
같습니다 ![]
배열은 배열이 아닙니다.
[ ] == ! [ ] ; // -> true
추상 항등 연산자는 양쪽을 숫자로 변환하여 비교하며, 양쪽은 서로 다른 이유로 숫자 0
이 됩니다. 배열은 진실이므로 오른쪽의 진실 값의 반대는 false
이며 0
으로 강제 변환됩니다. 그러나 왼쪽에서는 빈 배열이 먼저 부울이 되지 않고 숫자로 강제되고, 빈 배열은 참임에도 불구하고 0
으로 강제됩니다.
이 표현식을 단순화하는 방법은 다음과 같습니다.
+ [ ] == + ! [ ] ;
0 == + false ;
0 == 0 ;
true ;
또한 []
진실이지만 true
아닙니다.
!
)true
![]
와 같지 않지만 []
와도 같지 않습니다. Array는 true
와 같지 않지만 Array는 true
와 같지 않습니다. Array는 false
와 동일하지만 Array는 false
와 동일하지 않습니다.
true == [ ] ; // -> false
true == ! [ ] ; // -> false
false == [ ] ; // -> true
false == ! [ ] ; // -> true
true == [ ] ; // -> false
true == ! [ ] ; // -> false
// According to the specification
true == [ ] ; // -> false
toNumber ( true ) ; // -> 1
toNumber ( [ ] ) ; // -> 0
1 == 0 ; // -> false
true == ! [ ] ; // -> false
! [ ] ; // -> false
true == false ; // -> false
false == [ ] ; // -> true
false == ! [ ] ; // -> true
// According to the specification
false == [ ] ; // -> true
toNumber ( false ) ; // -> 0
toNumber ( [ ] ) ; // -> 0
0 == 0 ; // -> true
false == ! [ ] ; // -> true
! [ ] ; // -> false
false == false ; // -> true
! ! "false" == ! ! "true" ; // -> true
! ! "false" === ! ! "true" ; // -> true
다음을 단계별로 고려하십시오.
// true is 'truthy' and represented by value 1 (number), 'true' in string form is NaN.
true == "true" ; // -> false
false == "false" ; // -> false
// 'false' is not the empty string, so it's a truthy value
! ! "false" ; // -> true
! ! "true" ; // -> true
"b" + "a" + + "a" + "a" ; // -> 'baNaNa'
이것은 JavaScript의 구식 농담이지만 리마스터링되었습니다. 원본은 다음과 같습니다.
"foo" + + "bar" ; // -> 'fooNaN'
표현식은 'foo' + (+'bar')
로 평가되며, 이는 'bar'
숫자가 아닌 것으로 변환합니다.
+
)NaN
NaN
이 아니다 NaN === NaN ; // -> false
사양에서는 이 동작 뒤에 있는 논리를 엄격하게 정의합니다.
Type(x)
Type(y)
와 다른 경우 false를 반환합니다.Type(x)
숫자이면
x
NaN 이면 false를 반환합니다.y
NaN 이면 false를 반환합니다.- … … …
— 7.2.14 엄격한 평등 비교
IEEE의 NaN
정의는 다음과 같습니다.
보다 작음, 같음, 보다 큼, 순서가 지정되지 않은 네 가지 상호 배타적인 관계가 가능합니다. 마지막 경우는 적어도 하나의 피연산자가 NaN인 경우에 발생합니다. 모든 NaN은 자신을 포함한 모든 것과 순서 없이 비교됩니다.
— "IEEE754 NaN 값에 대해 모든 비교가 false를 반환하는 이유는 무엇입니까?" 스택오버플로우에서
Object.is()
및 ===
이상한 경우 Object.is()
두 값이 동일한 값을 갖는지 여부를 결정합니다. ===
연산자와 유사하게 작동하지만 몇 가지 이상한 경우가 있습니다.
Object . is ( NaN , NaN ) ; // -> true
NaN === NaN ; // -> false
Object . is ( - 0 , 0 ) ; // -> false
- 0 === 0 ; // -> true
Object . is ( NaN , 0 / 0 ) ; // -> true
NaN === 0 / 0 ; // -> false
JavaScript 용어에서 NaN
과 NaN
동일한 값이지만 엄격하게 동일하지는 않습니다. NaN === NaN
이 false인 것은 분명히 역사적인 이유 때문이므로 그대로 받아들이는 것이 더 나을 것입니다.
마찬가지로 -0
과 0
엄격하게 동일하지만 동일한 값은 아닙니다.
NaN === NaN
에 대한 자세한 내용은 위 사례를 참조하세요.
믿지 않으시겠지만…
( ! [ ] + [ ] ) [ + [ ] ] +
( ! [ ] + [ ] ) [ + ! + [ ] ] +
( [ ! [ ] ] + [ ] [ [ ] ] ) [ + ! + [ ] + [ + [ ] ] ] +
( ! [ ] + [ ] ) [ ! + [ ] + ! + [ ] ] ;
// -> 'fail'
대량의 기호를 여러 조각으로 나누면 다음 패턴이 자주 발생한다는 것을 알 수 있습니다.
! [ ] + [ ] ; // -> 'false'
! [ ] ; // -> false
그래서 false
에 []
추가해 보겠습니다. 그러나 여러 내부 함수 호출( binary + Operator
-> ToPrimitive
-> [[DefaultValue]]
)로 인해 올바른 피연산자를 문자열로 변환하게 됩니다.
! [ ] + [ ] . toString ( ) ; // 'false'
문자열을 배열로 생각하면 [0]
통해 첫 번째 문자에 액세스할 수 있습니다.
"false" [ 0 ] ; // -> 'f'
나머지는 분명하지만 i
는 까다롭습니다. i
in fail
'falseundefined'
문자열을 생성하고 인덱스 ['10']
의 요소를 가져와서 가져옵니다.
더 많은 예:
+ ! [ ] // -> 0
+ ! ! [ ] // -> 1
! ! [ ] // -> true
! [ ] // -> false
[ ] [ [ ] ] // -> undefined
+ ! ! [ ] / + ! [ ] // -> Infinity
[ ] + { } // -> "[object Object]"
+ { } // -> NaN
[]
사실이지만 true
아닙니다. 배열은 진실한 값이지만 true
와 같지는 않습니다.
! ! [ ] // -> true
[ ] == true // -> false
다음은 ECMA-262 사양의 해당 섹션에 대한 링크입니다.
!
)null
은 거짓이지만 false
아닙니다. null
이 잘못된 값이라는 사실에도 불구하고 false
와 동일하지는 않습니다.
! ! null ; // -> false
null == false ; // -> false
동시에 0
또는 ''
와 같은 다른 거짓 값은 false
와 같습니다.
0 == false ; // -> true
"" == false ; // -> true
설명은 이전 예제와 동일합니다. 해당 링크는 다음과 같습니다.
document.all
은 객체이지만 정의되지 않았습니다.
️ 이는 브라우저 API의 일부이며 Node.js 환경에서는 작동하지 않습니다.️
document.all
은 배열과 유사한 객체이고 페이지의 DOM 노드에 대한 액세스를 제공한다는 사실에도 불구하고 typeof
함수에 undefined
로 응답합니다.
document . all instanceof Object ; // -> true
typeof document . all ; // -> 'undefined'
동시에 document.all
undefined
과 동일하지 않습니다.
document . all === undefined ; // -> false
document . all === null ; // -> false
그러나 동시에:
document . all == null ; // -> true
document.all
특히 이전 버전의 IE에서 DOM 요소에 액세스하는 방법이었습니다. 표준이 된 적은 없지만 이전 JS 코드에서 널리 사용되었습니다. 표준이 새로운 API(예:document.getElementById
)로 진행되면서 이 API 호출은 더 이상 사용되지 않게 되었고 표준 위원회는 이를 어떻게 처리할지 결정해야 했습니다. API의 광범위한 사용으로 인해 그들은 API를 유지하기로 결정했지만 JavaScript 사양을 고의적으로 위반했습니다.undefined
Strict Equality Comparison을 사용할 때false
로 응답하고 Abstract Equality Comparison을 사용할 때true
로 응답하는 이유는 이를 명시적으로 허용하는 사양을 의도적으로 위반하기 때문입니다.— WhatWG - HTML 사양의 "사용되지 않는 기능 - document.all" — YDKJS의 "4장 - ToBoolean - 거짓 값" - 유형 및 문법
Number.MIN_VALUE
는 0보다 큰 가장 작은 숫자입니다.
Number . MIN_VALUE > 0 ; // -> true
Number.MIN_VALUE
는5e-324
입니다. 즉, 부동 소수점 정밀도 내에서 표현할 수 있는 가장 작은 양수입니다. 즉, 0에 최대한 가깝습니다. 이는 플로트가 제공할 수 있는 최상의 해상도를 정의합니다.이제 전체적으로 가장 작은 값은
Number.NEGATIVE_INFINITY
이지만 엄밀히 말하면 숫자는 아닙니다.— "JavaScript에서
0
Number.MIN_VALUE
보다 작은 이유는 무엇입니까?" 스택오버플로우에서
️ V8 v5.5 이하(Node.js <=7)에 버그가 있습니다.️
여러분 모두 짜증나는 undefound is not a function 에 대해 알고 계시지만, 이건 어떻습니까?
// Declare a class which extends null
class Foo extends null { }
// -> [Function: Foo]
new Foo ( ) instanceof null ;
// > TypeError: function is not a function
// > at … … …
이는 사양의 일부가 아닙니다. 현재는 버그가 수정된 것이므로 앞으로는 문제가 없을 것입니다.
최신 환경에서 이전 버그에 대한 이야기가 계속됩니다(Chrome 71 및 Node.js v11.8.0으로 테스트됨).
class Foo extends null { }
new Foo ( ) instanceof null ;
// > TypeError: Super constructor null of Foo is not a constructor
다음과 같은 이유로 이는 버그가 아닙니다.
Object . getPrototypeOf ( Foo . prototype ) ; // -> null
클래스에 생성자가 없으면 프로토타입 체인에서 호출됩니다. 그러나 부모에는 생성자가 없습니다. 혹시라도 null
이 객체라는 점을 분명히 하겠습니다.
typeof null === "object" ;
그러므로, 당신은 그것으로부터 상속받을 수 있습니다. (물론 OOP의 세계에서는 그러한 용어가 저를 이겼을 것입니다.) 따라서 null 생성자를 호출할 수 없습니다. 이 코드를 변경하는 경우:
class Foo extends null {
constructor ( ) {
console . log ( "something" ) ;
}
}
오류가 표시됩니다.
ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
그리고 super
추가하면:
class Foo extends null {
constructor ( ) {
console . log ( 111 ) ;
super ( ) ;
}
}
JS에서 오류가 발생합니다.
TypeError: Super constructor null of Foo is not a constructor
두 개의 배열을 추가하려고 하면 어떻게 될까요?
[ 1 , 2 , 3 ] + [ 4 , 5 , 6 ] ; // -> '1,2,34,5,6'
연결이 발생합니다. 단계별로 보면 다음과 같습니다.
[ 1 , 2 , 3 ] +
[ 4 , 5 , 6 ] [
// call toString()
( 1 , 2 , 3 )
] . toString ( ) +
[ 4 , 5 , 6 ] . toString ( ) ;
// concatenation
"1,2,3" + "4,5,6" ;
// ->
( "1,2,34,5,6" ) ;
4개의 빈 요소가 있는 배열을 만들었습니다. 그럼에도 불구하고 후행 쉼표로 인해 세 가지 요소가 있는 배열을 얻게 됩니다.
let a = [ , , , ] ;
a . length ; // -> 3
a . toString ( ) ; // -> ',,'
후행 쉼표 ("최종 쉼표"라고도 함)는 JavaScript 코드에 새 요소, 매개변수 또는 속성을 추가할 때 유용할 수 있습니다. 새 속성을 추가하려는 경우 해당 줄에 이미 후행 쉼표가 사용된 경우 이전 마지막 줄을 수정하지 않고 새 줄을 추가하면 됩니다. 이렇게 하면 버전 제어 차이점이 더 명확해지고 코드 편집이 덜 번거로워질 수 있습니다.
— MDN의 후행 쉼표
아래에서 볼 수 있듯이 배열 평등은 JS의 괴물입니다.
[ ] == '' // -> true
[ ] == 0 // -> true
[ '' ] == '' // -> true
[ 0 ] == 0 // -> true
[ 0 ] == '' // -> false
[ '' ] == 0 // -> true
[ null ] == '' // true
[ null ] == 0 // true
[ undefined ] == '' // true
[ undefined ] == 0 // true
[ [ ] ] == 0 // true
[ [ ] ] == '' // true
[ [ [ [ [ [ ] ] ] ] ] ] == '' // true
[ [ [ [ [ [ ] ] ] ] ] ] == 0 // true
[ [ [ [ [ [ null ] ] ] ] ] ] == 0 // true
[ [ [ [ [ [ null ] ] ] ] ] ] == '' // true
[ [ [ [ [ [ undefined ] ] ] ] ] ] == 0 // true
[ [ [ [ [ [ undefined ] ] ] ] ] ] == '' // true
위의 예를 매우 주의 깊게 살펴보아야 합니다! 동작은 사양의 섹션 7.2.15 추상 평등 비교에 설명되어 있습니다.
undefined
및 Number
Number
생성자에 인수를 전달하지 않으면 0
얻게 됩니다. 실제 인수가 없을 때 undefined
값은 형식 인수에 할당되므로 인수가 없는 Number
해당 매개 변수의 값으로 undefined
사용할 것으로 예상할 수 있습니다. 그러나 undefined
전달하면 NaN
얻게 됩니다.
Number ( ) ; // -> 0
Number ( undefined ) ; // -> NaN
사양에 따르면:
n
+0
으로 둡니다.n
이라고 놔둘까요? ToNumber(value)
.undefined
의 경우 ToNumber(undefined)
NaN
반환해야 합니다.해당 섹션은 다음과 같습니다.
argument
) parseInt
나쁜 놈이다 parseInt
특이한 점으로 유명합니다:
parseInt ( "f*ck" ) ; // -> NaN
parseInt ( "f*ck" , 16 ) ; // -> 15
설명: 이는 parseInt
가 알 수 없는 문자에 도달할 때까지 문자별로 계속 구문 분석하기 때문에 발생합니다. 'f*ck'
의 f
16진수 15
입니다.
Infinity
정수로 구문 분석하는 것은…
//
parseInt ( "Infinity" , 10 ) ; // -> NaN
// ...
parseInt ( "Infinity" , 18 ) ; // -> NaN...
parseInt ( "Infinity" , 19 ) ; // -> 18
// ...
parseInt ( "Infinity" , 23 ) ; // -> 18...
parseInt ( "Infinity" , 24 ) ; // -> 151176378
// ...
parseInt ( "Infinity" , 29 ) ; // -> 385849803
parseInt ( "Infinity" , 30 ) ; // -> 13693557269
// ...
parseInt ( "Infinity" , 34 ) ; // -> 28872273981
parseInt ( "Infinity" , 35 ) ; // -> 1201203301724
parseInt ( "Infinity" , 36 ) ; // -> 1461559270678...
parseInt ( "Infinity" , 37 ) ; // -> NaN
null
구문 분석에도 주의하세요.
parseInt ( null , 24 ) ; // -> 23
설명:
null
문자열"null"
로 변환하고 이를 변환하려고 합니다. 기수 0부터 23까지의 경우 변환할 수 있는 숫자가 없으므로 NaN을 반환합니다. 24에는 14번째 문자인"n"
이 수 체계에 추가됩니다. 31에는 21번째 문자인"u"
추가되어 전체 문자열을 디코딩할 수 있습니다. 37에서는 더 이상 생성할 수 있는 유효한 숫자 집합이 없으며NaN
이 반환됩니다.— "parseInt(null, 24) === 23… 잠깐, 뭐라고요?" 스택오버플로우에서
8진수를 잊지 마세요:
parseInt ( "06"