- 연산자 표현식은 연산자와 피연산자로 이루어진 표현식이다.
- 피연산자는 값으로 평가될 수 있는 표현식이어야 하며, 연산자 표현식 역시 값으로 평가될 수 있다.
- 자바스크립트의 연산자들은 피연산자들의 개수에 따라 다음과 같이 나눌 수 있다.
- 단항 연산자(unary operator): 피연산자가 1개인 연산자
- 이항 연산자(binary operator): 피연산자가 2개인 연산자
- 삼항 연산자(ternary operator): 피연산자가 3개인 연산자
- 연산자가 피연사자의 값을 변경한다면 **부수 효과(side effect)**가 있다고 한다.
**산술 연산자(arithmetic operator)**는 피연산자를 대상으로 수학적 계산을 하여 새로운 값을 반환하거나 피연산자의 값을 변경한다. 산술 연산이 불가능하다면 NaN
을 반환한다.
자바스크립트의 모든 이항 산술 연산자는 부수 효과가 없다.
연산자 | 의미 | 설명 |
---|---|---|
a + b |
더하기 | 피연산자 a 와 b 를 더한 값을 반환한다. |
a - b |
빼기 | 피연산자 a 에서 b 를 뺀 값을 반환한다. |
a * b |
곱하기 | 피연산자 a 를 b 번 곱한 값을 반환한다. |
a / b |
나누기 | 피연산자 a 를 b 로 나눈 값을 반환한다. |
a % b |
나머지 구하기 | 피연산자 a 를 b 로 나누었을 때 나머지 값을 반환한다. |
base ** exp |
거듭제곱하기 | 피연산자 base 를 exp 만큼 거듭제곱한다 |
연산자 | 의미 | 설명 |
---|---|---|
+a |
피연산자 a 가 number 타입이 아니면 number 타입으로 변환한 값을 반환한다.피연산자의 부호를 바꾸지는 않는다. |
|
-a |
피연산자의 부호를 반전한다. 피연산자가 a 가 number 타입이 아니면 number 타입으로 변환한 값을 반환한다. |
|
++a 혹은 a++ |
증가 | 부수 효과가 있어 피연산자의 값을 1 증가시킨다. |
--a 혹은 a-- |
감소 | 부수 효과가 있어 피연산자의 값을 1 감소시킨다. |
- 피연산자에
string
타입이 있다면 자바스크립트 엔진이 다른 피연산자를string
으로 변환하여 문자열 연결 연산(string concatenation)을 수행한다(8.8 문자열 연산자 참고). - 그렇지 않다면 피연산자들을
number
로 변환하여 더하기 연산을 수행한다.
"10" + 1; // "101"
10 + 1; // 11
undefined + 1; // NaN
null + 1; // 0
- 피연산자가
number
가 아니라면number
로 변환한 후 빼기 연산을 수행한다.
"10" - 2; // 8
10 - 2; // 8
10 - undefined; // NaN
10 - null; // 10
- 피연산자가
number
가 아니라면number
로 변환한 후 곱하기 연산을 수행한다.
"10" * 2; // 20
10 * 2; // 20
10 * undefined; // NaN
10 * null; // 0
- 피연산자가
number
가 아니라면number
로 변환한 후 나누기 연산을 수행한다.
"10" / 2; // 20
10 / 2; // 5
10 / undefined; // NaN
10 / null; // Infinity
10 / -0; // -Infinity
- ES7에 도입되었다.
- 피연산자가
number
가 아니라면number
로 변환한 후 거듭제곱 연산을 수행한다.
"10" ** 2; // 100
10 ** 2; // 100
10 ** undefined; // NaN
10 ** null; // 1
10 ** -0; // 1
- 피연산자가
number
가 아니라면number
로 변환한 후 단항 더하기/빼기 연산을 수행한다. - 단항 빼기 연산은 피연산자의 부호를 바꾼 값을 반환한다.
+false; // 0
-true; // -1
-3; // -3
+'hello'; // NaN
- 증가 연산자(increment opreator)
++
는 피연산자의 값을1
만큼 증가 시킨다. - 감소 연산자(decrement opreator)는
--
는 피연산자의 값을1
만큼 감소시킨다. - 증감 연산자는 피연산자 앞에 온다면 전위(prefix), 피연산자 뒤에 온다면 후위(postfix)이며 각각의 경우에 따라 연산이 다르다.
- 전위 증감 연산자의 경우, 피연산자의 값을 증가/감소 시킨 후 다른 연산을 진행한다.
- 후위 증감 연산자의 경우, 다른 연산을 수행한 후 피연산자의 값을 증가/감소시킨다.
/* 전위 연산자 */
let a = 1;
let b = a++; // a의 값 1을 b에 할당한 후, a의 값을 1 증가시킨다.
// a: 2, b: 1 // 최종적으로 a의 값은 2, b의 값은 1이 된다.
/* 후위 연산자 */
let a = 1;
let b = ++a; // a의 값을 1 증가시킨 후, a의 값 2를 b에 할당한다.
// a: 2, b: 2 // 최종적으로 a의 값은 2, b의 값은 2가 된다.
**비교 연산자(comparsion opreator)**는 두 피연산자를 비교하여 그 결과를 boolean
타입의 값으로 반환한다. 부수 효과는 없다.
연산자 | 설명 |
---|---|
== |
두 피연산자가 같으면 true 를 반환한다. |
!= |
두 피연산자가 같지 않으면 true 를 반환한다. |
=== |
두 피연산자의 값과 타입이 모두 같으면 true 를 반환한다. |
!== |
두 피연산자의 값 혹은 타입이 같지 않으면 true 를 반환한다. |
> |
왼쪽 피연산자가 오른쪽 피연산자보다 크면 true 를 반환한다. |
< |
왼쪽 피연산자가 오른쪽 피연산자보다 작으면 true 를 반환한다. |
>= |
왼쪽 피연산자가 오른쪽 피연산자보다 크거나 같으면 true 를 반환한다. |
<= |
왼쪽 피연산자가 오른쪽 피연산자보다 작거나 같으면 true 를 반환한다. |
- **동등 비교(loose equality) 연산자
==
**와 **일치 비교(strict equality) 연산자===
**는 피연산자가 같은 값으로 평가된다면true
를, 그렇지 않다면false
를 반환한다. - 이때 동등 비교 연산자는 느슨한 비교를 하고 일치 비교 연산자는 엄격한 비교를 한다.
- 느슨한 비교에서 자바스크립트 엔진은 암묵적 타입 변환을 하여 피연산자의 타입을 일치시키고 같은 값으로 평가되었는지 비교한다.
- 일치 비교는 두 피연산자의 타입과 값이 모두 일치할 경우
true
를 반환한다. 달리 말하여 암묵적 타입 변환을 하지 않고 같은 값으로 평가되었는지 비교한다.
'1' == 1; // true
'1' === 1; // false
즉, ==
와 ===
의 가장 큰 차이는 ==
는 암묵적 타입 변환을 하고 ===
는 그러지 않는다는 것이다.
NaN
은 자신과 일치하지 않는 유일한 값이다.
NaN == NaN; // false
NaN === NaN // false
- 어떤 숫자가
NaN
인지 알려면Number.isNaN
함수를 사용한다.Number.isNaN
은NaN
인 경우에만true
를 반환한다.
Number.isNaN(NaN); // true
Number.isNaN(1); // false
Number.isNaN(1 + 'hello'); // true
- 예외적으로, 동등 비교 연산자
==
는undefined
와null
을 형 변환하지 않는다. 피연산자undefined
와null
은 자신이나 서로를 동등 비교하는 경우를 제하면 모든 경우에서false
를 반환한다.
/* 자신을 비교하는 경우 */
undefined == undefined; // true
undefined === undefined; // true
null == null; // true
null === null; // true
/* 서로를 비교하는 경우 */
undefined == null; // true
undefined === null; // false
/* 그 외의 모든 경우 */
null == 0; // false
undefined == 0; // false
- 자바스크립트에서 양의
0
과 음의0
의 동등 비교와 일치 비교 결과는 모두true
이다.
0 == -0; // true
0 === -0; // true
- 두 피연산자가 모두
string
이라면 문자열 비교 연산을 한다(8.8.2 문자열 비교 연산 참고). - 그렇지 않다면 피연산자들을
number
로 변환하여 비교한다.- 피연산자가
number
로 변환되지 않는다면, 즉NaN
으로 변환된다면 식은 무조건false
를 반환한다.
- 피연산자가
/* string to number comparsion */
"1" > 2; // false
"3" > 1; // true
/* comparing boolean, null, undefined, NaN */
true >= false; // true
null >= 0; // true
undefined >= 0; // false
NaN >= 0; // false
null
과0
의 비교
null > 0; // false
null < 0; // false
null == 0; // false: 동등 연산자는 undefined와 null의 경우 형 변환 안 함
null >= 0; // true: 형 변환 하면 Number(null)은 0
null <= 0; // true: 상동
- **삼항 연산자(tenary opreator)**는 조건식의 평가 결과에 따라 반환할 값을 결정한다. 조건 연산자(conditional operator)라고도 한다.
- 조건식이
boolean
타입이 아니면boolean
타입으로 암묵적 변환한다.
condition ? a : b;
condition
이true
로 평가되면a
를,false
로 평가되면b
를 반환한다.
**논리 연산자(logical operator)**는 피연산자를 논리 연산하여 불리언 값이나 피연산자 값 중 하나를 반환한다. 부수 효과는 없다.
연산자 | 설명 |
---|---|
&& |
논리 AND |
` | |
! |
논리 NOT |
?? |
널 병합 |
단축 평가(short-circuit evaluation) 혹은 단락 평가는 표현식을 평가하는 도중 평가 결과가 확정된 경우 나머지 평가 과정을 생략하는 것을 말한다. 논리 연산에서는 표현식을 평가할 때 결과를 확정한 피연산자의 타입을 변환하지 않고 그대로 반환한다. 쉽게 판단하려면, 마지막으로 평가한 피연산자를 반환한다고 생각하면 된다.
expr1 && expr2
- 단락 평가(short-circuit evaluation)를 사용: 논리 연산자 표현식은 왼쪽에서 오른쪽으로 평가된다. 이때 왼쪽 연산자가 Falsy로 평가되면 오른쪽 피연산자는 평가하지 않고 표현식을 Falsy로 평가한다(왼쪽 피연산자를 반환한다).
- 논리 AND 연산은 피연산자 중 하나를 반환한다. 다음과 같이 일반화할 수 있다.
- 피연산자 중 하나가 Falsy라면 해당 피연산자를 반환한다.
- 그렇지 않다면, 오른쪽 피연산자를 반환한다.
"BLUE" && "YELLOW"; // "YELLOW": 두 연산자가 모두 Truthy, 오른쪽 피연산자인 "YELLOW" 반환
"BLUE" && false; // false: 한 연산자만 Falsy, 해당 피연산자인 false를 반환한다.
"BLUE" && null; // null: 상동
null && undefined; // null: 두 연산자가 모두 Falsy, 오른쪽 피연산자인 undefined 반환
expr1 || expr2
- 단락 평가(short-circuit evaluation)를 사용: 논리 연산자 표현식은 왼쪽에서 오른쪽으로 평가된다. 이때 왼쪽 피연산자가 Truthy로 평가되면, 오른쪽 피연산자는 평가하지 않고 표현식을 Truthy로 평가한다(왼쪽 피연산자를 반환한다).
- 논리 OR 연산은 피연산자 중 하나를 반환한다. 다음과 같이 일반화할 수 있다.
- 왼쪽 피연산자가 Falsy이면 오른쪽 피연산자를 반환한다.
- 그렇지 않다면, 왼쪽 피연산자를 반환한다.
"BLUE" || "YELLOW"; // "BLUE"
"BLUE" || false; // "BLUE"
0 || undefined; // undefined
!expr
- 논리 NOT 연산은 피연산자가 Falsy라면
false
를, Truthy라면true
를 반환한다.
let b1 = !"BLUE"; // false
let b2 = !false; // true
!
을 두 번 사용하면 피연산자의boolean
값으로 평가한 결과를 얻을 수 있다.
let b1 = !!'BLUE'; // true
let b2 = !!'false'; // true
'false'
는 string
타입이고 빈 문자열이 아니므로 Truthy이고 불리언 문맥에서 true
로 평가된다. 따라서 !'false'
는 false
, !!'false'
는 true
가 된다.
변수의 값이 객체라고 기대하고 프로퍼티를 접근하려 할 때, 변수의 값이 null
이나 undefined
즉 nullish value라면 TypeError
가 발생한다.
/* 변수의 값이 undefined인 경우 */
let a;
console.log(a.foo); // TypeError: Cannot read properties of undefined (reading 'foo')
/* 변수의 값이 null인 경우 */
let b;
console.log(b.foo); // TypeError: Cannot read properties of undefined (reading 'foo')
/* 변수가 빈 객체인 경우 */
let c;
console.log(c.foo); // undefined
변수의 값이 객체라면, 그것이 빈 객체({}
)여도 할당되지 않은 프로퍼티를 접근할 때 TypeError
를 발생시키지 않고 undefined
를 반환한다. 따라서 객체의 프로퍼티에 접근하기 전에 변수가 객체인지 확인해야만 한다.
let a;
let foo = a && a.foo;
console.log(foo); // undefined
처음 코드에서 a
의 값이 undefined
이므로 a.foo
는 TypeError
를 발생시켰다. 그러나 왼쪽 피연산자인 a
는 Falsy로 평가되고 단축 평가에 따라 오른쪽 피연산자는 평가하지 않고 오른쪽 피연산자 a
의 값인 null
이이 논리 연산의 결과가 된다.
ES11의 옵셔널 체이닝 연산자
?.
로 개선된 문법을 사용할 수 있다. 8.4.4 옵셔널 체이닝을 참고한다.
const getFoo = (obj) => obj.foo;
getFoo(); // TypeError: Cannot read properties of undefined (reading 'foo')
함수에 인수를 전달하지 않으면 매개변수에는 undefined
가 할당된다. 여기서 인수 없이 getFoo()
를 호출하여 obj
에는 undefined
가 할당되었고, 따라서 obj.foo
로 nullish value에 프로퍼티 접근을 시도하여 TypeError
가 발생했다.
이 경우 임의의 초기값을 가지도록 설정해줄 수 있다.
const getFoo = (obj) => {
obj = obj || {}
return obj.foo;
};
getFoo(); // undefined
왼쪽 피연산자인 obj
가 Falsy로 평가되므로 단축 평가에 따라 오른쪽 피연산자 {}
이 이 논리 연산자 표현식의 값이 된다. obj
에 {}
이 새롭게 할당되었고 obj.foo
로 선언되지 않은 프로퍼티에 접근해도 undefined
를 반환하므로 TypeError
를 발생시키지 않는다.
ES11의 null 병합 연산자
??
로 개선된 문법을 사용할 수 있다. 8.4.5 널 병합 연산을 참고한다.
ES11에 도입된 옵셔널 체이닝(Optional Chaining) 연산자 ?.
는 왼쪽 항의 피연산자가 nullish라면 undefined
를 반환하고 그렇지 않다면 오른쪽 항의 프로퍼티 참조를 수행한다.
- 문제 상황: nullish value에 프로퍼티 접근하면
TypeError
발생
let a;
let foo = a.foo; // // TypeError: Cannot read properties of undefined (reading 'foo')
- 기존 논리 AND 연산자
&&
사용하여 해결
let a;
let foo = a && a.foo;
console.log(foo); // undefined
- ES11의 옵셔널 체이닝
?.
사용하여 개선
let a;
let foo = a?.foo;
console.log(foo); // undefined
ES11에 도입된 널 병합(nullish coalescing) 연산자 ??
는 왼쪽 항의 피연산자가 nullish라면 오른쪽 항의 피연산자를 반환하고 그렇지 않다면 왼쪽 항의 피연산자를 반환한다.
- 문제 상황: 원하는 값이라면 그대로 할당하고 원하는 값이 아니면 재할당하고 싶음
let name;
if (name === undefined)
name = 'unknown';
console.log(name); // 'unknown'
- 기존의 논리 OR 연산자
||
사용하여 개선
let name;
name = name || 'unknown';
console.log(name); // 'unknown'
하지만 첫번째 라인에서 name
이 ''
로 초기화되었다고 해보자. 프로그래머는 nullish가 아니라면 모두 유효한 값이라고 생각하여 name
에 그대로 ''
가 재할당되기를 기대할 수 있다.
let name = '';
name = name || 'unknown'; // ''는 nullish가 아니므로 false를 그대로 재할당하기를 기대함
console.log(name); // 'unknown'
그러나 ''
는 Falsy이므로, ||
의 단축 평가에 따라 name
에는 'unknown'
이 담기게 된다. nullish가 Falsy에 속하므로 ||
로는 프로그래머가 기대하는 작업을 할 수 없다.
- ES11의 널 병합 연산
??
사용
let name = '';
name = name ?? 'unknown';
console.log(name); // ''
''
는 nullish가 아니므로 프로그래머의 기대처럼 name
에 재할당된다.
false || 'TEDDY'; // 'TEDDY'
false ?? 'TEDDY'; // false
연산자 | 왼쪽 피연산자가 | 반환 |
---|---|---|
?? |
nullish value(null , undefined )인 경우 |
오른쪽 피연산자를 반환한다. |
` | ` |
false
는 nullish value가 아니므로, 위 경우의??
식에서는 왼쪽 피연산자false
가 반환된다.false
는 Falsy value이므로, 위 경우의||
식에서는 오른쪽 피연산자'TEDDY'
가 반환된다.
??
는 명확하게 null
과 undefined
를 처리할 때 유용하다. ?.
는 null
이나 undefined
일 수 있는 객체의 프로퍼티에 접근할 때 TypeError
가 발생하지 않도록 한다.
연산자 | 설명 |
---|---|
& |
비트 AND |
` | ` |
^ |
비트 XOR |
~ |
비트 NOT |
<< |
비트 왼쪽 시프트 |
>> |
비트 오른쪽 시프트 |
>>> |
비트 부호 없는 오른쪽 시프트 |
**할당 연산자(assignment operator)**는 오른쪽 항에 있는 피연산자의 평가 결과를 왼쪽 항에 있는 변수에 할당한다. 따라서 부수 효과가 있다.
연산자 | 설명 |
---|---|
x = y |
x = y |
x += y |
x += y |
x -= y |
x -= y |
x *= y |
x = x * y |
x /= y |
x = x / y |
x %= y |
x = x % y |
x **= y |
x = x ** y |
x <<= y |
x = x << y |
x >>= y |
x = x >> y |
x >>>= y |
x = x >>> y |
x &= y |
x = x & y |
x ^= y |
x = x ^ y |
`x | = y` |
x &&= y |
x && (x = y) |
`x | |
x ??= y |
x ?? (x = y) |
- 할당문은 값으로 평가되는 표현식인 문이므로 할당된 값으로 평가된다.
var x;
console.log(x = 10); // 10
- 따라서 할당문은 표현식으로서 다른 변수에 할당할 수도 있다. 이것을 체이닝(chaining)이라고 한다.
const a = b = 10;
console.log(a); // 10
- 할당식을 여러 개 체이닝하는 경우 각각의 할당식은 오른쪽에서 왼쪽으로 차례대로 평가한다.
const a = b = c;
// const a = ( b = c ); 와 같음.
+
은 피연산자에string
타입이 있으면 string concatenation을 수행한다.
'HELLO ' + 'WORLD'; // "HELLO WORLD"
let str = 'HELLO';
str += 'WORLD'; // str은 "HELLO WORLD"
<
,>
,<=
,>=
연산은 피연산자가 모두string
이면 문자열 비교 연산을 한다.
'a' > 'b'; // false
'a' > '3'; // true
'abc' > 'abd'; // false
쉼표 연산자 ,
는 왼쪽에서 오른쪽으로 피연산자를 평가한 후, 가장 마지막 피연산자의 평가 결과를 반환한다.
let x = (2, 3);
console.log(x); // 3
let a, b, c;
a = 1, b = 2, c = 3; // 3
그룹 연산자는 피연산자를 ()
로 감싸 자신의 피연산자인 표현식을 가장 먼저 평가한다. 그룹 연산자는 우선순위가 가장 높으므로 연산자의 우선순위를 조절할 수 있다.
9 * 1 + 3; // 12
9 * (1 + 3); // 36
delete
연산자는 객체의 프로퍼티를 삭제하므로 부수 효과가 있다.
delete object.property;
delete object[propertyKey];
delete objectName[index];
연산자 | 반환 | 설명 |
---|---|---|
delete |
boolean |
객체의 프로퍼티를 삭제한다. 성공적으로 삭제했다면 true 를, 그렇지 않다면 false 를 반환한다. |
- 다만, 존재하지 않는 프로퍼티를 삭제하려 할 때에도
true
를 반환한다.
let o = { name: 'umi', age: 6 };
console.log(delete o.name); // true
- 전역 객체의 경우
var
로 선언한 전역 변수를 제외한 모든 프로퍼티를 삭제할 수 있다.
typeof
연산자는 피연산자의 데이터 타입을 문자열로 반환한다.
typeof operand
typeof (operand)
연산자 | 반환 | 설명 |
---|---|---|
typeof |
string 혹은 undefined |
피연산자의 타입을 나타내는 문자열(number , string , boolean , undefined , object , function , symbol , bigint )을 반환한다. |
typeof null; // object
typeof
에 선언하지 않은 식별자를 피연산자로 전달하면 ReferenceError
를 일으키지 않고 undefined
를 반환한다.
typeof x; // undefined
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/void
void (expression)
void expression
연산자 | 반환 | 설명 |
---|---|---|
void |
undefined |
피연산자를 평가한다. |
프로퍼티
가 객체
에 존재하는지 여부를 반환한다.
프로퍼티 in 객체
연산자 | 반환 | 설명 |
---|---|---|
in |
boolean |
프로퍼티 가 객체 에 존재하면 true 를, 그렇지 않다면 false 를 반환한다. |
const COLORS = [ 'BLUE', 'YELLOW' ];
'BLUE' in COLORS; // true
let user = { name: 'lana' };
'lana' in user; // true
객체
가 생성자 함수와 연결된 인스턴스인지 여부를 반환한다.
객체 instanceof 생성자함수
연산자 | 반환 | 설명 |
---|---|---|
instanceof |
boolean |
객체 가 생성자함수 와 연결된 인스턴스라면 true 를, 그렇지 않다면 false 를 반환한다. |
new Date() instanceof Date; // true
new Date() instanceof Object; // true