Before start
연산자(operator)는 어플리케이션에서 input 정보를 연산(operation)할 때 이용된다. 연산을 input을 이용한 process(처리, 계산)라고 하면 이해에 도움이 된다. 즉 연산자는 input을 처리/계산하는 데 사용된다.
Operators
Arithmetic Operator | +, -, *, /, %, **
산술 연산자를 말한다. 산술 연산자는 우리에게 가장 친숙한 연산자이며, 두개의 피연산자를 필요로 한다(binary operator).
2+5+6 이라는 식을 예로 들어보자. 우리는 이 식에서 (2+5) + 6의 과정을 거쳐 값을 계산한다. 먼저 괄호(bracket) 안에 있는 2+5를 계산하는데 2와 5가 피연산자가 되고 +가 연산자가 된다. 즉 +는 두 개의 피연산자(2, 5)를 받는다. 이 계산이 끝난 다음에는 7+6을 계산한다. 마찬가지로 7과 6이 피연산자가 되고, +가 연산자가 된다.
숫자끼리의 연산 뿐만 아니라, 숫자+문자열의 연산도 가능하다. '7'+120; 를 작성하면 '7120'이 값으로 변환되고 데이터 타입은 string이 된다. 숫자-문자열의 연산도 가능한데 '7'-2;를 작성하면 이번에는 숫자 5가 값으로 변환된다.
plus(+)외에 subtract(-), multiply(*), divide(/), remain(%), exponent(**)가 있다. exponent는 ES7에 추가된 문법 사항으로 이전에는 Math.pow() 함수를 이용했다.
Unary Operator | +, -, !, !!
두 개의 피연산자를 필요로하는 binary operator와 달리, unary opertaor는 한 개의 피연산자만을 필요로 한다.
+와 -는 산술 연산자 더하기, 빼기와 같아 보이지만 다르다.
unary operator +는 더하기와 달리, 하나의 피연산자를 받는다. 이 때 피연산자가 숫자가 아니면, 피연산자를 숫자로 변환했을 때 갖게되는 값을 생성한다. 숫자 데이터를 피연산자로 받으면, 그 숫자가 양수임을 의미한다. -는 하나의 숫자를 피연산자로 받았을 때, 숫자에 -1을 곱한 값을 갖는다.
+null; //0
+undefined; // NaN
!는 피연산자의 데이터 타입을 boolean 값으로 변환한 값의 부정을 생성한다. 예를 들어서 undefined은 boolean 값으로 false를 갖고있으니 !undefined; 은 true를 반환한다. !!연산자는 부정을 다시 부정하기 때문에 피연산자의 데이터 타입을 boolean 값으로 변환한다. !!undefined은 undefined의 boolean값인 false를 생성한다.
!undefined; // true
!!undefined; // false
Assignment Operator | =, +=, -=, *=...
a+=2는 a=a+2의 축약형이다.
Increment/ Decrement Operator: ++, --
let a = 0;
a++;
console.log(a); // 1
console.log(a++); // 1 here!
// -------------------- //
let a = 0;
++a;
console.log(a); // 1
console.log(++a); // 2 here!
++와 -- 연산자는, 각각 피연산자의 값을 1씩 증가/ 감소 시킨다. 이때 연산자의 위치에 따라 코드를 처리하는 방식에 차이가 있다. 즉 a++와 ++a를 처리하는 방식이 다르다.
++가 피연산자의 뒤에 있으면 ++ 연산은 뒷전으로 밀리고 다른 연산을 먼저 수행한다. 즉, console.log(a++)라고 작성하면 console에 일단 a를 출력하는 연산을 한 뒤에, a의 값을 증가시킨다는 것이다. 현재 a의 값은 1이므로 1을 출력한 뒤에야 a는 2가 된다. 반면 ++가 피연산자에 앞에 있으면 ++ 연산을 먼저 하고 나머지 연산을 처리한다. console.log(++a)는 a의 값을 2로 증가시킨 후에 a를 출력하는 것이다.
Relational Operator | >, >=, <, <=
두 숫자 데이터의 크기를 비교한 뒤, true 또는 false의 값을 생성한다.
Equailty Operator | ==, ===, !=, !==
두 값이 서로 같은지 비교한 뒤 true 또는 false를 생성한다.
== 연산자(equality opreation)는 두 데이터의 type이 다르더라도 true가 반환될 수 있다. 예를 들어 '2' == 2;는 true이고, null == undefined도 true이다. 하지만 === 연산자(strict equality operator)는 두 데이터의 type이 다르면 false를 반환한다. '2' === 2;는 false고, null === undefined도 false이다.
+) == 연산자는 서로 다른 타입의 연산자를 받았을 때, 연산자의 타입을 변환하기 때문이다.
'2' == 2; // true
null == undefined // true
'2' === 2; // false
null === undefined; // false
Logical Operator | &&, ||, !, !!
&&(AND)와 ||(OR)는 표현식(expression)을 피연산자로 받는다.
- 표현식(expression)은 값을 반환하는 문(statement)을 말하고, 문은 코드가 실행되는 단위를 말한다. 문은 주로 semi colon(;)으로 구분된다.
- let a;와 같은 선언(declaration)은 표현식이 아니기 때문에, 논리 연산자의 피연산자가 될 수 없다.
- &&와 ||가 함께 사용된 식에서는 &&가 먼저 처리된다.
&&와 ||는 각각 연산의 결과로 다음 값을 반환한다.
&&는 두 피연산자 중 falsy한 피연산자의 값을 반환하고, 모든 피연산자가 truthy 하다면 두번째 피연산자의 값을 반환한다.
||는 두 피연산자 중 truthy한 피연산자의 값을 반환하고, 모든 피연산자가 falsy하다면 두번째 피연산자의 값을 반환한다.
+) truthy, falsy
const falsy = [false, 0, -0, 0n, "", null, undefined, NaN];
// truthy한 값은 falsy 하지 않은 모든 값을 말한다.
&&는 두 피연산자가 모두 true일 때, ||는 둘 중 하나라도 true일 때 true가 된다는 설명이 더 익숙할 수도 있다. 그러나 이는 정확한 설명이 아니다. 피연산자가 항상 boolean 값을 반환하지는 않기 때문이다. 그럼에도 둘 중 하나가 true면 true 같은 설명이 익숙한 것은 조건문 때문이다.
자바스크립트 코드는 조건문(if~ 등)을 실행할 때, Boolean context에 진입한다. 이 컨텍스트에서는 모든 값을 boolean으로 변환한다. 예를 들어 "hello" 같은 string 값은 true로, null 이나 undefined 값은 false로 변환한다.
아래의 예제 코드를 살펴보자.
// 조건문 if
let obj1 = { name: 'obj1' };
let obj2 = { name: 'obj2' };
if (obj1 && obj2) {
console.log('both true!'); // both true가 출력된다
}
obj1과 obj2는 객체 값을 저장하고 있고, 객체는 truthy한 값이기 때문에 object1 && object2 는 true && true가 된다. &&는 두 피연산자 중 falsy한 값을 찾아내서 반환하는데, 만약 모든 값이 truthy 하다면, 마지막 연산자의 값이 반환된다. 마지막 연산자의 값이 true이기 때문에 object1 && object2는 true가 된다.
and 연산자는 첫번째로 만나는 피연산자가 falsy한 값이라면, 두번째 피연산자는 평가조차 하지 않고 조건문을 빠져나온다. or 연산자도 마찬가지이다. 첫번째로 만나는 피연산자가 truthy한 값이라면 두번째 피연산자는 평가하지 않는다.
반면 and와 or가 첫번째로 만나는 피연산자가 각각 truthy, falsy한 값일 때에는 두번째 피연산자의 반환 결과를 확인해야만 한다.
let andEval = obj1 && obj2;
let orEval = obj1 || obj2;
andEval; // {name: 'obj2'}
orEval; // {name: 'obj1'}
조건문 밖에서는 조금 다른 일이 벌어진다. && 연산자는 두 피연산자 중 falsy한 값을, 피연산자의 값이 모두 truthy하다면 마지막 피연산자의 값을 반환한다고 했다. 그래서 andEval은 true가 아니라 obj2의 실제 값을 반환한다.
또 다른 점이 있다. 조건문 안에서 각 논리 연산자가 첫번째 피연산자로 받은 표현식의 반환값이 true인지, false인지에 따라 두번째 표현식을 평가할지 말지를 결정했다면, 조건문 밖에서는 첫번째 표현식의 평가 결과에 상관 없이, 두번째 표현식을 평가하지 않는다. 이것을 단축평가(short circuit evaluation)라고 부른다.
Nullish Coalescing(==combine) Operator | ??
??는 ||의 한계를 보완하기 위해 ES11에 추가된 문법 사항이다. ||는 falsy한 값을 모두 false로 취급한다면, ??는 nullish한 값들만 false로 취급한다. falsy한 값과 nullish한 값은 다음과 같다.
const falsy = [0, -0, null, undefined, '', false];
const nullish = [null, undefined];
??가 ||의 한계를 보완한다는 것은 어떤 의미일까? 아래 예제 코드를 살펴보자.
let num = 0;
num || 'no value'; // no value!
num ?? 'no value'; // 0
||는 falsy한 값을 모두 false로 취급하고, 평가 결과가 truthy인 표현식을 반환한다. 예제 코드에서 num은 0이라는 값이 있음에도 불구하고, 0 자체가 false로 취급되기 때문에 OR 연산자는 'no value'를 반환한다.
Optional chaining Operator | ?.
let kitty = { name: '😺', owner: { name: 'taylor' } };
let puppy = { name: '🐶' };
kitty.owner.name // 'taylor'
object에서 dot notation을 이용하면 중첩된 객체의 정보에 접근할 수 있다. 그런데 만약 우리가 puppy의 주인 정보에 접근하려고 하면 어떤 일이 발생할까? 강아지는 아직 주인이 없기 때문에 undefined이라는 값을 반환해주면 좋겠지만 JS엔진은 에러(Cannot read properties of undefined!)를 던진다. 이런 에러의 발생을 막기 위해 && 논리 연산자가 사용되었다.
let owner = puppy && puppy.owner && puppy.owner.name;
owner // undefined
puppy라는 객체가 계속 반복되기 때문에 이를 간단하게 나타낼 수 있는 연산자를 ES11에서 추가했는데, 이것이 바로 ?.이다. 아래의 코드는 && 연산자를 이용한 코드와 똑같이 작동한다.
owner = puppy?.owner?.name;
Priority
연산에는 우선순위가 있다. 더하기보다 곱하기를 먼저 해야하는 것처럼 말이다. 프로그래밍 언어에는 우리가 배웠던 연산자보다 훨씬 많은 연산자를 가지고 있기 때문에 우선순위를 다 외울 필요는 없고, 필요할 때마다 MDN에서 제공하는 table을 확인하면 된다. 사실 이렇게까지 할 필요도 없는 게 최우선순위를 가지는 연산자가 (괄호)이기 때문에 먼저 해야하는 연산을 괄호로 묶어주기만 하면 우선순위를 크게 신경쓸 일은 없다.
'Study > JavaScript' 카테고리의 다른 글
Function & Functional Programming (0) | 2022.03.26 |
---|---|
Control flow statement (0) | 2022.03.25 |
Data Type (0) | 2022.03.24 |
Variables (0) | 2022.03.24 |
What is Javascript? (0) | 2022.03.24 |