Thanks for inventing Javascript

Bam·2023년 9월 13일
0

자바스크립트 밈

목록 보기
3/4

오늘 가져온 밈짤은 이것입니다.

벌써 어질어질하죠? 하나씩 풀어서 해석해보겠습니다.


typeof NaN is 'number'

NaN은 숫자가 아님을 의미하는 Number 내장 객체의 프로퍼티입니다.

그런데 숫자가 아님을 의미하지만 typeof 연산자를 통해 타입을 확인하면 number 타입으로 나오게 됩니다.

이 이유는 NaN이 숫자 데이터로 취급되기 때문입니다.
즉, 숫자 연산을 수행했지만 그 결과를 숫자로 나타낼 수 없는 경우에 NaN을 띄우는거죠. 따라서 NaN은 숫자가 아님이 아닌, '숫자로 표현할 수 없는 수'가 더 정확한 표현이고 number 타입으로 취급되는 것 입니다.


9999999999999999 is 10000000000000000

9999_9999_9999_9999 is 1_0000_0000_0000_0000

9999조 9999억 9999만 99991경으로 취급됩니다.

자바스크립트는 double-precision floating-point(배정도 부동 소수점)를 사용합니다. 이때 9999조~라는 숫자는 double-precision floating-point로 표현할 수 있는 값의 범위를 넘어서기 때문에 double-precision floating-point로 표현할 수 있는 가장가까운 숫자인 1경으로 반올림되는 것 입니다.

바로 뒤에 나올 문제가 똑같이 배정도 부동 소수점 연산에 대한 이야기를 다룹니다. 더 자세한 내용은 다음 문제도 함께 참조해주세요.


0.5 + 0.1 == 0.6 is true, 0.1 + 0.2 == 0.3 is false

이 문제도 위에서 말한 배정도 부동 소수점 방식과 긴밀한 연관이 있습니다.

0.1과 같은 수는 우리가 일상에서 사용하는 10진법수 입니다. 그리고 컴퓨터는 연산에 전기 신호가 흘렀다 꺼졌다를 나타내는 10 두 가지 숫자만을 사용하는 2진법을 사용합니다. 이때 0.1이라는 10진수는 2진수로 변환하는 과정에서 다음과 같은 무한 소수가 됩니다.

0.000110011001100110011...

인간의 뇌와는 달리 컴퓨터는 정해진 자원(메모리) 내에서 데이터를 표시하고 연산하기 때문에 위와같은 무한 소수를 모두 표시할 수 없습니다. 따라서 표현할 수 있는 자신의 범위 내에서 반올림하여 표시하게 됩니다.

실제로 toString을 이용해서 0.1을 2진수로 나타내면 아래와 같은 유한 소수가 됩니다.

이런 반올림 과정에서 미세한 차이가 발생하게 되고, 그 결과로 0.1 + 0.2 == 0.3 is false와 같은 문제가 발생하게 되는 것 입니다.

자바스크립트 밈에 껴있긴하지만, 배정도 방식을 이용하는 프로그래밍 언어(대표적으로 자바)들에서 공통적으로 발생하는 문제입니다.

실제로 0.1 + 0.2와 같은 연산이 필요하다면 소수점 자리를 자르는 toFixed() 메소드를 활용하면 문제를 해결할 수 있습니다.


Math.max() is -Infinity, Math.min() is Infinity

Math.max()는 인수에서 가장 큰 수를, Math.min()은 가장 작은 수를 반환하는 메소드입니다.

근데 인수를 전달하지 않을경우 max()는 -Infinity를 반환하고 min()은 Infinity를 반환합니다. 말이 안되는 것 같지 않나요?

그 이유는 두 메소드의 내부 로직에 숨겨져있습니다.

Math.max()는 초기값으로 -Infinity를 두고 비교를 합니다. -Infinity가 거의 모든 수보다 작기 때문입니다. 그래서 인수로 아무것도 주지않으면 초기값인 -Infinity가 그대로 반환되기 때문에 위와같은 결과가 나타나는 것 입니다.
Math.min()도 마찬가지로 반대입니다. 더 작은 수를 반환할 것이므로 초기값으로 Infinity를 두고 비교를 하게 되고, 인수가 없으면 초기값을 그대로 반환하게 됩니다.

[]과 {}

[] + [] is ''

+ 연산자는 피연산자의 자료형에 따라 아래와 같은 역할을 수행합니다.
먼저, [] + []은 배열과 배열의 +연산을 합니다. 그리고 배열은 자바스크립트에서 객체의 한 종류죠. 따라서 + 연산 과정에서 빈 배열을 문자열로 변환하고 문자열 연결을 하게 됩니다.

이때 빈 배열을 문자열로 변경하면 빈 문자열''이 됩니다. 따라서 결과적으로는 '' + ''가 되므로, 결과도 ''가 됩니다.

[] + {} is '[object Object]'

[] + {}도 마찬가지로 둘 다 객체(배열과 객체)이므로 문자열 변환 후 연결을 수행하게 됩니다.
앞의 빈 배열은 전과 마찬가지로 빈 문자열''로 변화합니다. 그리고 문제는 뒤의 빈 객체{}인데 이것은 toString()의 동작과 관련이 있습니다.

toString()은 모든 객체에 존재하는 메소드이며, 해당 객체가 문자열로 표현되었을 때의 예상값을 출력해줍니다.

이때 값이 없으면 해당 타입의 기본형을 출력해줍니다. 여기서 빈 객체의 기본형은 [object Object]이므로 '' + [object Object]가 되어서 '[object Object]'라는 결과가 나오게 되는 것 입니다.

{} + [] is 0

마지막으로 {} + []는 조금 복잡합니다. 먼저 앞의 {}빈 객체가 아닌 빈 블록으로 해석됩니다. 즉, {} + []이 식은 + []와 다를 것이 없습니다. 그리고 뒤에 오는 +[]는 빈 문자열이 될 것같지만 아닙니다. []를 갖는 + 단항 연산자가 됩니다.

예전에 바나나 밈을 소개하면서 단항 연산자를 소개했는데요. 간략히 말하자면 피연산자를 숫자로 변환하게 됩니다.

그리고 빈 배열은 숫자로 강제 변환하면 0이 되기 때문에 {} + []0이 되는 것 입니다.

추가적으로 {} + {}도 있는데요. 앞의 {}는 마찬가지로 빈 블록으로 해석되고 뒤의 +{}는 단항 연산자가 되어 강제 변환이 이루어지는데 빈 객체의 강제 변환은 [object Object]입니다. 따라서 숫자로 나타낼 수 없는 연산이 이루어지기 때문에 결과는 NaN이 됩니다.

true는 1이다?

ture + true + true === 3 is true

ture + true + true === 3는 간단합니다.

자바스크립트에서 true1로 취급됩니다. while(1)이 무한루프를 만드는 것을 생각해보면 쉽습니다. 따라서 true가 연산 과정에서 1로 취급되어 3이라는 결과가 나타나게 됩니다. 반면, false0으로 취급됩니다.

true - true is 0

방금도 언급했지만 true는 1로 취급된다고했죠? 따라서 당연히 true - true0이 됩니다.

true == 1 is true, true === 1 is false

true1로 취급됩니다. 1은 Truthy 한 값으로 간주되기 때문이죠. 이것이 방금 위의 두 문제의 핵심이었습니다.

이번 문제의 해답은 =====의 동작에 있습니다.

=====는 비교 연산자 입니다. 예전에 비교 연산자포스트에서도 다뤘었는데요.

==는 양쪽의 자료형을 동일하게 만든 후 비교합니다. 따라서 true == 1에서는 어느 한쪽을 boolean이나 number로 만들어서 비교합니다. 그 결과는 당연히 둘이 똑같겠죠. true는 1이고, 1은 true로 취급되니까요.

반면, ===는 자료형과 값을 검사해서 둘 다 일치하지 않으면 false를 뱉게 됩니다. 그래서 true와 1이 내부적으로 동일하게 취급되더라도 trueboolean이고, 1number이므로 false라는 결과를 나타내게 됩니다.


(!+[]+[]+![]).length is 9

이번 문제에도 단항 연산자와 강제 변환이 사용됩니다.

! + []!+[]로 나뉩니다. 그리고 위에서 말했듯이 +[]0이고요. 따라서 !0이 되는데 여기서 0은 false로 간주되고, 논리 부정이므로 true가 됩니다.

위 결과에 따라서 현재 식은 true + [] + ![]이 됩니다.

true + []를 계산할 차례입니다. + 연산자에 따라 true는 true 그대로 쓰이고, 빈 배열은 빈 문자열''로 변환됩니다. 따라서 ture + []의 결과는 'true'가 됩니다.

이제 식은 'true' + ![]입니다. 앞의 피연산자가 문자열이므로 이번에는 연결을 수행합니다. 'true'는 잠시 내버려두고 뒤의 ![]를 확인합시다. 자바스크립트에서 빈 배열은 Truthy한 값으로 간주됩니다. 즉 ![]!true라는 것이죠. 따라서 ![]의 결과는 false가 되고 문자열 연결을 수행하기 때문에 결과적으로는 'truefalse'라는 결과가 나오게 됩니다.

(!+[]+[]+![]).length === 'truefalse'.length

자, 그러면 이제 아시겠죠? 'truefalse'는 9글자니까 (!+[]+[]+![]).length의 결과는 당연히 9가 나오게 됩니다.

9+"1" is '91', 91 - '1' is 90

9+"1"은 간단하죠? 두 피연산자 중 하나가 문자열이면 연결을 수행합니다. 따라서 '91'입니다.

91 - '1'- 연산자의 동작에 따릅니다. 좌우항 피연산자의 자료형에 따라 다른 동작을 하는 +와는 다르게 -는 숫자 연산만을 수행합니다. 따라서 좌우항을 모두 숫자로 변환한뒤에 연산을 하게 됩니다. 그래서 91 - 1의 결과를 보여주는 것이죠.

그래서 코테 등에서 문자열 -> 숫자 변환을 메소드 사용하는 대신 '123' - 0와 같이 간단하게 줄일 수 있습니다.


[] == 0 is true

이 내용은 다른 유명한 밈이 있었어서 다뤘던 적이 있었으므로 넘어가겠습니다.

0개의 댓글