최근 면접에서 자바스크립트 연산과 형변환 문제를 받게되었다.
뭐 애초에 면접이 너무 떨려서 풀지 못함은 물론이요 나중에 답 물어볼 생각도 못했다.
이런 질문이 있었다는 것조차 기억해내지 못하다가 우연히
사이트를 보게되었는데,
정리해두면 괜찮겠다 싶어서 정리해보기로했다.
answer : 1
더하기 연산에서 string이 없을 경우 boolean은 숫자로 변환된다.
answer : 3
배열은 세개의 빈 슬롯으로 구성된다. 마지막은 트레일링 콤마.
answer : "1,2,34,5,6"
배열의 덧셈은 배열을 문자열로 바꾼 뒤 concat으로 동작한다.
간혹 "1,2,3,4,5,6"의 답을 얻기위해
[1, 2, 3] + [, 4, 5, 6]
이나
[1, 2, 3, ""] + [4, 5, 6]
같은 코드를 설계하는 건 가독성을 해친다.
원래 의도에 가장 근접한 접근은
[...[1, 2, 3], ...[4, 5, 6]]
정도 되겠다.
answer : false
자바스크립트의 원시타입 중 Number는 64비트 부동소수점 형식의 2진수로 표시된다.
일부 소수는 10진법에서 2진법으로 변환될 때 무한소수가 되어버려
사용자의 의도와 다르게 계산될 수 있다.
answer : 2
쉼표 연산자라고 불리는 이것은
'각각의 피연산자를 왼쪽에서 오른쪽 순서로 평가하고, 마지막 연산자의 값을 반환합니다.' 라는 사전적 정의를 갖고있다.
쉼표 연산자라는 이름은 잘 모른채 여러 변수를 다중으로 선언할 때 주로 보게됐었다.
마지막 값이 리턴된다는 특징을 알게되었다.
answer : false
NOT 연산자는 값을 반대의 boolean값으로 변환한다.
값이 boolean 자료형이 아니라면 Truthy 혹은 Falsy 변환을 거친다.
느낌표가 두개라면 반대의 반대, 즉 값의 boolean 평가값이 그대로 정답이된다.
answer : 1
[]는 truthy하다. 고로 !![]는 true로 평가되며
true값에 단항 더하기 +를 붙이면 숫자 표현인 1로 변환된다.
answer : 5
parseInt(numericalString, radix)에서 numericalString 부분은
string이 아닐 경우 string 형식으로 변환한다.
요 블로그에 number to string에 대한 여러가지 실험이 있는데
String(0.05); // => '0.05'
String(0.005); // => '0.005'
String(0.0005); // => '0.0005'
String(0.00005); // => '0.00005'
String(0.000005); // => '0.000005'
String(0.0000005); // => '5e-7'
다음과 같이 문자열 변환에서 소수 일곱번째 자리부터 지수표기법으로 전환된다.
신기한 것은 블로그 마지막에parseInt(999999999999999999999)
에 대한 질문이 있는 것이다.
자바스크립트의 Number 최대범위인 2^53 -1(9007199254740991)을 아득히 초과했는데,
콘솔로 여러가지를 찍어보았다.
String(9007199254740991); // => '9007199254740991'
String(90071992547409919); // => '90071992547409920'
String(900719925474099199); // => '900719925474099200'
.
.
.
String(999999999999999999999); // => '1e+21'
String(111111111111111111111); // => '111111111111111110000'
String(1111111111111111111111); // => '1.1111111111111111e+21'
2^53 -1 부터는 반올림으로 부정확한 처리를 하다가
10^21이상의 수에서는 exponential 표기법을 쓰고 있다.
이와 관련한 ECMAScript 내용을 살펴보면 String(value)는 다음 알고리즘을 따른다.
String(value)의 2.b 규칙에 따라 ToString(value)를 거친다.
ToString(Number)는 Number::toString ( x )을 반환하는데
결국 우리가 넣은 숫자들은 이 규칙을 따르는 것이다.
Number::toString ( x )의 핵심은 결국 다음과 같다.
x가 10^(-6) 과 10^21 사이에 있다면 일반적인 표현을 사용하고
그 외에는 the code unit 0x0065 (LATIN SMALL LETTER E)를 포함한
string-concatenation 이 발생한다 (지수 표기법)
또한 소수 작업을 할때는 정수로 변환하거나, Math 라이브러리 사용을 권장한다.
answer : false
ESLint 플러그인 같은 코딩 컨벤션에 익숙해지면 일치 연산자(strict equality, ===)만 쓰게 되어 동등 연산자는 머릿속에서 지워진다. 동등 연산자는 일치 연산자와 비교하는 대상의 자료형이 다를 경우 형변환을 한 후 비교한다는 차이가 있다.
형변환 알고리즘은 다음을 따른다.
살펴보자면
너무나 당연하게 같은 자료형의 같은 놈들은 true, 아니면 false이다. 참조타입은 참조에 따른 비교를 실시한다.
(다만 NaN == NaN // => false
이다.)
null과 undefined의 비교는 true이다.
Number와 String의 비교는 String을 Number로 형변환 후 비교한다.
Boolean과 다른 자료형의 비교는 Boolean을 Number로 형변환 후 다시 비교한다.
Object와 Number혹은 String의 비교는 Object를 원시타입으로 변환 후 다시 비교한다.
문제의 경우는 4번 규칙에 의해 true를 1로 변환한 후 (1 == "true")
3번 규칙에 의해 (1 == "NaN")이 되어 답이 false가 된다.
(숫자로 변환할 수 없는 String 값을 Number로 형변환 시 NaN이 된다. 고로 Number("true")는 NaN이다.)
answer : 5
오우쒸 말도안돼
다음 문서에 따르면 strict mode가 아닐 때, 0으로 시작하면서 뒤에 8미만의 숫자들로 구성된 표현은 8진법을 따른다.(parsed as octal number)
ex) 0777 == 511 // => true
(0777 = 7x(8^2) + 7x(8^1) + 7x(8^0) = 511)
010은 그럼 8이고 03은 3이겠네?
실화다.
answer : 0
암시적 형변환은 +의 경우 string-concatenation이 수학적 덧셈에 우선하여
String으로의 형변환이 우선권을 가진다.
다른 연산자(-,/,*,% 등)는 Number로의 형변환이 우선시된다.
따라서 -연산자에 의해 "" - - "" 은 0 - - 0으로 변하고
0 - -0이 된다.
따라서 답은 0
주의사항으로 -를 붙여서 --로 표현하면 --0 부분에서 syntax error가 발생한다.
--(감소연산자)는 뒤의 대상으로 값이아닌 참조변수가 와야하기 때문.
answer : 0
+연산자는 string이 없으면 number가 우선 시 된다.
Number(null) => 0
answer : NaN
0으로 나누기 문제는 검색해보면 여러 수학적 논의가 쏟아지더라....
프로그래밍에서는 정지를 막기 위한 처리를 해놨다고 생각하면 될 거 같다.
JS에서는 나름 극한의 개념을 갖고 n/0을 다음과 같이 처리한다.
다른 언어에서는 0으로 나누는 행위 자체가 예외를 발생시키기도 하지만
JS는 그 특성상 일단 값을 내어준다.
n>0 => Infinity
n=0 => NaN
n<0 => -Infinity
그리고 ECMA Script3 까지는 NaN과 Infinity가 수정 가능했지만
ES5부터 읽기 전용 상수가 되었다고한다.
answer : false
ECMAScript의 Number Type 명세에 따르면 2^1024이상의 수는 지수 표기법으로도 표현되지 않고 Infinity로 변환된다.
13번 문제에도 설명했듯, 1/0은 Infinity이며 10 ** 1000도 Infinity로
1/0 === 10**1000 은 true이다. 문제의 답은 false
answer : SyntaxError
문제 11번의 설명과 MDN-증가연산자 의 설명처럼
증감 연산자는 유효한 참조인 피연산자에 붙을 수 있다.
값인 true에 붙을 경우 SyntaxError가 발생한다.
해설지에는 여러가지 케이스들을 정리해줬다.
true++; // -> SyntaxError
1++; // -> SyntaxError
"x"++; // -> SyntaxError
null++; // -> SyntaxError
undefined++; // -> NaN
$++; // -> NaN
console.log++; // -> NaN
NaN++; // -> NaN
true 값을 가진 변수에 ++를 실시하면 2로 변환된다.
let _true = true;
_true++;
_true; // -> 2
answer : -1
암시적 형변환 규칙에 따라 - 연산자에서는 String과 Number가 있을 경우 Number로의 형변환이 우선시 된다.
따라서 0 - 1 => -1
answer : "00"
암시적 형변환 규칙에 따라 - 연산에서 (0-0)으로 변환되고,
+ 연산자에서는 string-concatenation이 우선 발생하여
0 + "0"은 "00"이 된다.
answer : "00"
암시적 형변환 규칙에 따라 ("true" - 0) 연산에서 "True"는 Number로 형변환된다.
Number("True")는 NaN이므로 이후의 연산은 모두 NaN으로 처리된다.
answer : 0
논리연산자 NOT은 truthy 값을 false로, falsy 값을 true로 바꿔준다.
!5는 false로 변환된다.
+연산자에서 boolean은 Number로 변환되기때문에
답은 0 + 0 이 된다.
answer : ""
3번 문제의 설명처럼
배열의 + 연산은 string-concatenation이 우선 발생함에 따라
"" + "" => ""가 된다.
answer : "33"
자바스크립트 구문은 왼쪽에서 오른쪽으로 실행된다.
+ 연산을 순서대로 실행하면 숫자 덧셈과 string-concatenation이 차례로 실행된다.
answer : "number"
ECMAScript에 따르면
NaN은 Number이면서 "Not-aNumber"임이 정의되어있다.
Number의 특정 값(NaN, +0, -0)에 있어 이상한 것은 Object.is 메소드와 ===이 다르게 동작한다는 점이다.
Object.is(0, -0) //=> false
Object.is(NaN, NaN) //=> true
0 === -0 //=>true
NaN === NaN //=>false
answer : 0
String이 없기에 모두 Number로 형변환된다.
undefined는 falsy이지만 Number(undefined)는 NaN이기 때문에 이후의 계산은
NaN + 0 으로 정답은 NaN이 된다.
answer : ""
&& 연산자를 if문의 조건식에만 썼다면 놓치기 쉽지만, &&는 그 자체로는 boolean으로의 형변환이 없다.
왼쪽이 truthy하다면 왼쪽 값을, falsy하다면 오른쪽 값을 그대로 반환한다.
빈 문자열은 falsy값이므로 ""가 그대로 반환된다.
answer : 0
MDN의 Logical NOT 설명을 보면 NOT연산자는 항상 따라오는 값을 boolean으로 형변환하며, 이때 NaN은 false로 평가한다.
Not연산자가 두개이므로 !!NaN은 false이다.
+는 더하기 연산자 외에도 단항 더하기(Unary plus)라는 것이 있다. Number외의 값 앞에 붙으면 Number로의 형변환을 일으킨다.
따라서 +!!NaN는 0이다.
다음으로 0 * ""은 곱하기 연산자의 Number 형변환 우선 규칙에 따라 0 * 0으로 처리한다.
마지막으로 0 - - [,]은 빼기 연산의 규칙에 따라 빈 배열을 0으로 형변환하고,
0 - -0의 연산이 이어진다.
따라서 답은 0