Operators(연산자)
문자열 연결: 이항 연산자 +
+ 연산자는 덧셈 뿐만 아니라 문자열 연결에도 사용됩니다.
let s = "my" + "string";
alert(s); // mystring
| cs |
이 때 연결되는 피연산자 중 어느 하나라도 문자열이면 나머지 하나도 문자열로 취급됩니다.
alert( '1' + 2 ); // "12"
alert( 2 + '1' ); // "21"
| cs |
그러나 이 연산은 좌에서 우의 방향으로 이루어지기 때문에 피연산자가 세 개 이상일 경우엔 조금 얘기가 다릅니다.
alert(2 + 2 + '1' ); // "41" and not "221"
| cs |
+ 연산자의 경우에만 문자열 연결에도 사용이 되며, 나머지 다른 사칙연산자 -, *, /는 숫자에만 적용되고, 설사 따옴표를 넣어 문자열로 만들었더라도 다시 수로 변환하여 계산됩니다.
alert( 2 - '1' ); // 1
alert( '6' / '2' ); // 3
| cs |
숫자 변환: 단항 연산자 +
+는 단항 연산자의 기능도 있습니다.
숫자에 붙이면 단순히 + 기호로서 아무런 변화도 일으키지 않지만 숫자가 아닌 것에 붙이면 숫자로 변환하는 기능이 있습니다.
// 숫자에는 영향 없음
let x = 1;
alert( +x ); // 1
let y = -2;
alert( +y ); // -2
// non-numbers를 변환
alert( +true ); // 1
alert( +"" ); // 0
| cs |
문자를 숫자로 변환하는 작업은 자주 필요합니다. 예를 들자면 HTML 폼 영역에서 값을 얻어오면 그건 대부분 문자열이므로 그걸 그냥 더하면 문자열 연결이 됩니다.
let apples = "2";
let oranges = "3";
alert( apples + oranges ); // "23", 문자열 연결
| cs |
그러나 +연산자로 숫자 변환하면 덧셈이 됩니다.
let apples = "2";
let oranges = "3";
// 단항+로 변환하고 이항 +로 덧셈
alert( +apples + +oranges ); // 5
// 더 긴 방법으로는
alert( Number(apples) + Number(oranges) ); // 5
| cs |
+ 기호가 여러개 겹쳐 나오는게 이상해 보일 수도 있지만 단항 연산자 +가 이항 연산자 +보다 우선 순위를 가지므로 무난히 처리 됩니다.
연산자 순위표
Assignment
할당 기호인 =도 연산자의 일종입니다. 이 연산자의 우선순위는 굉장히 낮은 편에 속하는 3입니다. 따라서 변수에 x=2*2+1과 같은 문장을 실행했을 때 연산이 먼저 이루어진 후 변수에 할당합니다.
아래와 같은 연쇄 할당도 가능합니다.
let a, b, c;
a = b = c = 2 + 2;
alert( a ); // 4
alert( b ); // 4
alert( c ); // 4
| cs |
이런 chain assignment 시에는 우에서 좌로 평가되므로 먼저 2+2의 연산이 이루어지고 c, b, a의 순으로 할당되어 결과적으로 모두 같은 값을 가지게 됩니다.
+, *같은 연산자들이 값을 반환하는 것처럼, 연산자 = 도 값을 반환합니다.
예를 들어 x=value와 같은 문장은 x에 value를 할당하고 그 값을 리턴하는 역할도 합니다.
아래의 예를 살펴 봅시다.
let a = 1;
let b = 2;
let c = 3 - (a = b + 1);
alert( a ); // 3
alert( c ); // 0
| cs |
가운데 줄을 보면 괄호 안에서 b+1의 결과를 a에 할당하고, 그 결과인 3을 다시 피연산자로 사용하여 결과를 내놓은 것을 볼 수 있습니다.
좀 이상하게 보이는 코딩법이며, 우리 스스로는 저런 식으로 사용하지 않아야 하겠지만 종종 저런 식으로 작성된 코드가 보이는 경우가 있으니 알아두시기 바랍니다.
나머지: %
C계열의 언어나 다른 많은 언어에서처럼 %는 나머지값을 구하는데 쓰입니다.
거듭제곱: **
뒤의 수가 지수로 쓰이는 거듭제곱입니다.
alert( 2 ** 2 ); // 4 (2 * 2)
alert( 2 ** 3 ); // 8 (2 * 2 * 2)
alert( 2 ** 4 ); // 16 (2 * 2 * 2 * 2)
| cs |
지수로는 정수가 아닌 것도 가능합니다.
alert( 4 ** (1/2) ); // 2 제곱근
alert( 8 ** (1/3) ); // 2 세제곱근
| cs |
증가, 감소
다른 많은 언어에서처럼 ++, --는 변수의 값을 1씩 증가/감소시키는데 쓰입니다.
(5++처럼 숫자에 직접 쓰이면 에러가 납니다)
++, --는 변수의 앞, 뒤에 모두 올 수 있는데, 차이가 좀 있습니다.
let counter = 1;
let a = ++counter; // 증가 후 할당
alert(a); // 2
| cs |
let counter = 1;
let a = counter++; //할당 후 증가
alert(a); // 1
| cs |
그렇다면 다른 연산의 중간에 오게되면 어떻게 될까요
let counter = 1;
alert( 2 * ++counter ); // 4
| cs |
let counter = 1;
alert( 2 * counter++ ); // 2
| cs |
사실 이런 방식은 가능은 합니다만 가독성을 해치므로, 아래처럼 한 줄에 하나의 액션을 갖는 코드를 작성하도록 합시다.
let counter = 1;
alert( 2 * counter );
counter++;
| cs |
비트와이즈 연산자
대부분의 언어에서처럼 2진수 레벨에서 32비트 정수를 다루는 비트연산자를 제공합니다.
AND(&)
OR(|)
XOR(^)
NOT(~)
LEFT SHIFT(<<)
RIGHT SHIFT(>>)
ZERO-FILL RIGHT SHIFT(>>>)
더 알고 싶으신 분은 여기 참고.
Modify-in-place
하나의 변수를 연산에 사용하고 그 결과를 원래의 변수에 재할당할 때
+=, *=, /=, -= 등 처럼 모든 수학 연산자와 비트연산자에 사용합니다.
이런 연산자는 = 연산자와 우선 순위가 같게 취급되어, 우항이 먼저 연산된후 할당됩니다.
let n = 2;
n *= 3 + 5;
alert( n ); // 16 (우항 먼저 연산, n *= 8과 )
| cs |
Comma
쉼표 ,는 드물게 사용되는 독특한 연산자입니다. 여러 개의 표현식을 평가하고 마지막 값을 반환하는데 쓰입니다.
let a = (1 + 2, 3 + 4);
alert( a ); // 7 (the result of 3 + 4)
| cs |
위의 코드에서 먼저 1+2를 평가하고 그 결과는 버립니다. 다음 3+4를 평가하고 그 값을 반환합니다.
중요한 것은 이 콤마는 우선 순위가 굉장히 낮기 때문에 괄호로 묶어 주어야 한다는 점입니다. 만약 괄호를 생략하고 a=1+2,3+4 로 작성한다면 a에는 3을 할당하고 7은 무시됩니다.
마지막 값만 반환하고 나머지는 버리는 이런 연산자가 왜 필요할까요?
이런 방식은 한 줄에 여러 개의 액션을 갖는 표현을 넣어 코드를 간단하게 작성할 때 사용되곤 합니다.
// 한 줄에 3개의
for (a = 1, b = 3, c = a * b; a < 10; a++) {
...
}
| cs |
위와 같은 방식은 많은 자바스크립트 프레임웍에 사용되는데, 보통 가독성을 떨어뜨리므로 사용에 신중해야 하겠습니다.
비교
비교의 연산자로는 다른 언어들처럼<, >, <=, >=, ==, != 를 사용하며 비교 연산의 결과로서 true, fasle를 리턴합니다.
alert( 2 > 1 ); // true (correct)
alert( 2 == 1 ); // false (wrong)
alert( 2 != 1 ); // true (correct)
let result = 5 > 4; // 비교결과를 변수에
alert( result ); // true
| cs |
문자열 비교
문자열은 사전 순서(사실 유니코드 순)에 따라 한 글자씩 이루어집니다.
alert( 'Z' > 'A' ); // true
alert( 'Glow' > 'Glee' ); // true
alert( 'Bee' > 'Be' ); // true
| cs |
먼저 첫 글자를 비교하고, 같다면 다음 문자로 이어지며, 세번째와 같이 앞은 같은데 더 긴 것이 있다면 긴 것이 크다고 판정합니다.
서로 다른 타입간의 비교
서로 다른 타입 사이에 비교가 이루어지면 숫자로 변환하여 판단합니다.
alert( '2' > 1 ); // true, string '2' 는 숫자 2가 됨
alert( '01' == 1 ); // true, string '01'은 숫자 1이 됨
alert( true == 1 ); // true
alert( false == 0 ); // true
| cs |
여기서 한 가지 알아두어야 할 재미있는 결과가 있습니다.
다음의 결과를 봅시다.
let a = 0;
alert( Boolean(a) ); // false
let b = "0";
alert( Boolean(b) ); // true
alert(a == b); // true!
| cs |
a는 숫자 0이므로 false, b는 문자열이므로 true로 각각 판정되었는데, 직접 비교시에는 숫자로 변환되어 둘다 0으로 판정됩니다. 알아두도록 합시다.
strict equality
보통의 동등 비교 ==는 0과 false를 구분하지 못하는 단점이 있습니다.
alert( 0 == false ); // true
alert( '' == false ); // true
| cs |
0도 빈 문자열도 모두 false로 판정됩니다.
이는 == 연산자가 다른 타입의 비교시에 숫자로 변환하기 때문인데, 그 둘을 구분하려면 엄격한 동등 연산자(strict equality operator)인 ===을 사용합니다.
그러면 숫자로 타입변환하는 일 없이 직접 비교를 하게 됩니다.
alert( 0 == '' ); // true
alert( 0 === '' ); // false
| cs |
마찬가지로 !=도 !==을 사용하여 엄격하게 판정 가능합니다.
null, undefined와의 비교
alert( null === undefined ); // false
alert( null == undefined ); // true
| cs |
==, === 비교
둘을 비교하면 타입이 다르므로 엄격한 비교시에는 다르다고 판정되며, 일반적 비교시에는 같은 것으로 판정합니다.그 외의 비교 연산자들(<, >, <=, >=)
이 때 null은 0, undefined는 1로 변환됩니다. 그 결과로 이 둘이 다른 값과 비교될 때 직관과 어긋나는 케이스들이 있어 소개합니다.이상한 결과: null vs 0
null과 0을 비교해 봅시다.
alert( null > 0 ); // (1) false
alert( null == 0 ); // (2) false
alert( null >= 0 ); // (3) true
| cs |
0보다 큰 것도 아니고 0도 아닌데 0 이상이라는군요. 그냥 봐서는 도무지 말이 안 되는데 이것은 ==이 다른 비교 연산자들과 다르게 작용하기 때문입니다.
==비교시에 null과 undefined는 다른 변환과정 없이 같은 것으로 판정하기로 정해 두었습니다. 그래서 서로는 같은 것이지만 다른 어떤 것과도 같지 않습니다.
반면에 다른 비교 연산시에는 0으로 변환됩니다. 그래서 위의 예 1번은 false, 3번은 true입니다.
비교 불가능한 undefined
alert( undefined > 0 ); // false (1)
alert( undefined < 0 ); // false (2)
alert( undefined == 0 ); // false (3)
| cs |
0과 어떻게 비교를 해봐도 모두 false가 나옵니다.
일단 (1), (2)의 경우에는 undefined가 NaN으로 변환되기 때문이고, (3)의 경우 undefined는 null과만 같은 것으로 정했다고 바로 전에 살펴봤습니다.
이런 문제를 피하려면?
이런 특수한 사례들을 모두 염두에 두고 있어야 할까요? 점차 자바스크립트를 다루면서 이런 문제가 익숙해져서 잘 피할 수 있게도 되겠지만 그보다도 이를 피할 수 있는 확실한 방법이 있습니다.
그것은 undefined/null과의 비교를 ===로만 하는 것입니다. 그리고 확신이 생기기 전까지는 null/defined값을 가질 수 있는 변수에 >, >=, <, <= 비교를 피하시길 바랍니다.
댓글 없음:
댓글 쓰기