모던 자바스크립트 튜토리얼을 읽으며 정리한 글이다.
2.7 형 변환
함수와 연산자에 전달되는 값은 대부분 적절한 자료형으로 자동 변환된다. 이런 과정을 "형 변환(type conversion)"이라고 한다.
문자형으로 변환
문자형으로의 형 변환은 문자형의 값이 필요할 때 일어난다.
String(value) 함수를 호출해 전달받은 값(value)을 문자열로 변환할 수 있다.
숫자형으로 변환
숫자형으로의 변환은 수학과 관련된 함수와 표현식에서 자동으로 일어난다.
Number(value) 함수를 사용하면 주어진 값(value)을 숫자형으로 변환할 수 있다.
한편, 숫자 이외의 글자가 들어가 있는 문자열을 숫자형으로 변환하려고 하면, 그 결과는 NaN이 된다.
아래는 숫자형으로 변환 시 적용되는 규칙이다.
전달받은 값 | 형 변환 후 |
undefined | NaN |
null | 0 |
true and false | 1과 0 |
string | 문자열의 처음과 끝 공백이 제거된다. 공백 제거 후 남아있는 문자열이 없다면 0, 그렇지 않다면 문자열에서 숫자를 읽는다. 변환에 실패하면 NaN이 된다. |
불린형으로 변환
이 형 변환은 논리 연산을 수행할 때 발생한다.
Boolean(value)를 호출하면 주어진 값(value)을 불린형으로 변환할 수 있다.
불린형으로 변환 시 적용되는 규칙은 다음과 같다.
- 숫자 0, 빈 문자열, null, undefined, NaN과 같이 직관적으로 "비어있다고" 느껴지는 값들은 false가 된다.
- 그 외의 값은 true로 변환된다.
전달받은 값 | 형 변환 후 |
0, null, undefined, NaN, "" | false |
그 외의 값 | true |
★ 주의: 문자열 "0"은 true이다.
PHP 등의 일부 언어에서 문자열 "0"을 false로 취급한다. 그러나 자바스크립트에선 비어 있지 않는 문자열은 언제나 true이다.
2.8 기본 연산자와 수학
단항, 이항, 피연산자
피연산자(operand): 연산자가 연산을 수행하는 대상 = 인수(argument)
단항(unary) 연산자: 피연산자를 하나만 받는 연산자
이항(binary) 연산자: 두 개의 피연산자를 받는 연산자
수학
- 덧셈 연산자 +
- 뺄셈 연산자 -
- 곱셈 연산자 *
- 나눗셈 연산자 /
- 나머지 연산자 %
- 거듭제곱 연산자 **
나머지 연산자 %
나머지 연산자를 사용한 표현식 'a % b'는 a를 b로 나눈 후 그 나머지(remainder)를 정수로 반환해준다.
거듭제곱 연산자 **
거듭제곱 연산자(exponentiation operator)를 사용한 'a ** b'는 a를 b번 곱한 값이 반환된다.
이항 연산자 '+'와 문자열 연결
이항 연산자 '+'의 피연산자로 문자열이 전달되면 덧셈 연산자는 덧셈이 아닌 문자열을 병합(연결)한다. 따라서 이항 연산자 '+'를 사용할 때는 피연산자 중 하나가 문자열이면 다른 하나도 문자열로 변환된다는 점에 주의해야 한다.
이처럼 이항 덧셈 연산자 '+'는 문자열 연결과 변환이라는 특별한 기능을 제공한다. 다른 산술 연산자와 오직 숫자형의 피연산자만 다루고, 피연산자가 숫자형이 아닌 경우에 그 형을 숫자형으로 바꾸는 것과 대조적이다.
단항 연산자 '+'와 숫자형으로의 변환
숫자에 단항 덧셈 연산자를 붙이면 이 연산자는 아무런 동작을 하지 않는다. 그러나 피연산자가 숫자가 아닌 경우에는 숫자형으로의 변환이 일어난다.
단항 덧셈 연산자는 짧은 문법으로도 Number(...)와 동일한 일을 할 수 있게 해준다.
let apples = "2";
let oranges = "3";
// 이항 덧셈 연산자가 적용되기 전에, 두 피연산자는 숫자형으로 변화합니다.
alert( +apples + +oranges ); // 5
// `Number(...)`를 사용해서 같은 동작을 하는 코드를 작성할 수 있지만, 더 기네요.
// alert( Number(apples) + Number(oranges) ); // 5
위 식은 단항 덧셈 연산자가 먼저 문자열을 숫자로 변환시키고, 이항 덧셈 연산자가 그 결과들을 더해주고 있다.
그런데 왜 이항 덧셈 연산자가 적용되기 전에 단항 덧셈 연산자가 먼저 적용될까? 그 이유는 연산자 우선순위를 통해서 알 수 있다.
연산자 우선순위
자바스크립트에서 정의한 연산자 우선순위가 마음에 들지 않는다면, 괄호를 사용하면 된다. 괄호는 모든 연산자보다 우선순위가 높기 때문에 자바스크립트에서 정의한 연산자 우선순위를 무력화시킨다.
자바스크립트는 다양한 연산자를 제공하는데, 이 모든 연산자엔 우선순위가 매겨져 있다. 우선수위 숫자가 클수록 먼저 실행된다. 순위가 같으면 왼쪽부터 시작해서 오른쪽으로 연산이 수행된다.
할당 연산자
무언가를 할당할 때 쓰이는 '='도 연산자이다. 이 연산자는 할당(assignment) 연산자라고 불리는데, 우선순위는 3으로 아주 낮다.
복합 할당 연산자
let n = 2;
n += 5; // n은 7이 됩니다(n = n + 5와 동일).
n *= 2; // n은 14가 됩니다(n = n * 2와 동일).
alert( n ); // 14
이런 복합 할당 연산자는 산술 연산자와 비트 연산자에도 적용할 수 있다. 예) /=, -=
복합 할당 연산자의 우선순위는 할당 연산자와 동일하다. 따라서 대부분 다른 연산자가 실행된 후에 복합 할당 연산자가 실행된다.
증가/감소 연산자
숫자를 하나 늘리거나 줄이는 것은 자주 사용되는 연산이다. 자바스크립트에서는 이런 연산을 해주는 연산자를 제공한다.
- 증가(increment) 연산자 ++
- 감소(decrement) 연산자 --
'++'와 '--' 연산자는 변수 앞이나 뒤에 올 수 있다.
- 후위형(postfix form): 'counter++'와 같이 피연산자 뒤에 올 때, 증가/감소 전의 기존 값 반환
let counter = 1;
let a = counter++; // (*) ++counter를 counter++로 바꿈
alert(a); // 1
(*)로 표시한 줄의 후위형 'counter++'는 counter를 증가시키긴 하지만, 증가 전의 기존 값을 반환한다. 따라서 alert는 1을 표시한다.
- 전위형(prefix form): '++counter'와 같이 피연산자 앞에 올 때, 증가/감소 후의 새로운 값 반환
let counter = 1;
let a = ++counter; // (*)
alert(a); // 2
(*)로 표시한 줄의 전위형 '++counter'는 counter를 증가시키고 새로운 값 2를 반환한다. 따라서 alert는 2를 표시한다.
증가, 감소 연산자에 대한 내용을 정리하면 아래와 같다.
- 반환 값을 사용하지 않는 경우라면, 전위형과 후위형엔 차이가 없다.
let counter = 0;
counter++;
++counter;
alert( counter ); // 2, 위 두 라인은 동일한 연산을 수행합니다.
- 값을 증가시키고 난 후, 증가한 값을 바로 사용하려면 전위형 증가 연산자를 사용하면 된다.
- 값을 증가시키지만, 증가 전의 기존 값을 사용하려면 후위형 증가 연산자를 사용하면 된다.
cf) ++/-- 연산자를 표현식 중간에 사용하는 것도 가능하다. 이때, 증가/감소 연산자의 우선순위는 다른 대부분의 산술 연산자보다 높다.
비트 연산자
비트 연산자(bitwise operator)는 인수를 32비트 정수로 변환하여 이진 연산을 수행한다.
아래는 비트 연산 시 쓰이는 연산자 목록이다.
- 비트 AND( & )
- 비트 OR( | )
- 비트 XOR( ^ )
- 비트 NOT( ~ )
- 왼쪽 시프트(LEFT SHIFT)( << )
- 오른쪽 시프트(RIGHT SHIFT)( >>)
- 부호 없는 오른쪽 시프트(ZERO-FILL RIGHT SHIFT)( >>> )
비트 연산자는 저수준(2진 표현)에서 숫자를 다뤄야 할 때 쓰이므로 흔하게 쓰이진 않는다. 웹 개발 시엔 이런 일이 자주 일어나지 않기 때문에 비트 연산자를 만날 일은 거의 없다. 하지만 암호를 다뤄야 할 땐 비트 연산자가 유용하다.
쉼표 연산자
쉼표 연산자 ','는 여러 표현식을 코드 한 줄에서 평가할 수 있게 해준다. 이때 표현식 각각이 모두 평가되지만, 마지막 표현식의 평가 결과만 반환되는 점에 유의해야 한다.
코드를 짧게 쓰려는 의도로 가끔 사용된다.
let a = (1 + 2, 3 + 4);
alert( a ); // 7 (3 + 4의 결과)
cf) 쉼표 연산자의 연산자 우선순위는 매우 낮다. 할당 연산자 '='보다 더 낮다. 따라서 위 예시에선 괄호가 중요한 역할을 한다. 괄호가 없으면 'a = 1+2, 3+4'에서 '+'가 먼저 수행되어 'a = 3, 7'이 된다. 할당 연산자 '='는 쉼표 연산자보다 우선순위가 높기 때문에 'a = 3'이 먼저 실행되고, 나머지(7)은 무시도니다. '(a = 1+2), 3+4'를 연산한 것처럼 된다.
과제
전위형과 후위형
아래 코드가 실행된 후, 변수 a, b, c, d엔 각각 어떤 값들이 저장될까?
let a = 1, b = 1;
let c = ++a; // ?
let d = b++; // ?
a = 2, b = 2, c = 2, d = 1
할당 후 결과 예측하기
아래 코드가 실행되고 난 후, a와 x엔 각각 어떤 값이 저장될까?
let a = 2;
let x = 1 + (a *= 2);
a = 4, x = 5
형 변환
아래 표현식들의 결과를 예측해 보자.
"" + 1 + 0 = "10" // (1)
"" - 1 + 0 = -1 // (2)
true + false = 1
6 / "3" = 2
"2" * "3" = 6
4 + 5 + "px" = "9px"
"$" + 4 + 5 = "$45"
"4" - 2 = 2
"4px" - 2 = NaN
7 / 0 = Infinity
" -9 " + 5 = " -9 5" // (3)
" -9 " - 5 = -14 // (4)
null + 1 = 1 // (5)
undefined + 1 = NaN // (6)
" \t \n" - 2 = -2 // (7)
덧셈 고치기
아래 코드는 사용자에게 숫자 2개를 입력받은 다음 그 합을 보여준다. 그런데 의도한 대로 예시가 작동하지 않는다. 프롬프트 창에 세팅한 기본값을 수정하지 않은 경우 덧셈의 결과는 12가 된다. 왜 그럴까? 예시가 제대로 동작하도록 코드를 수정해 보자. 결과는 3이 되어야 한다.
let a = prompt("덧셈할 첫 번째 숫자를 입력해주세요.", 1);
let b = prompt("덧셈할 두 번째 숫자를 입력해주세요.", 2);
alert(a + b); // 12