JS02 - 표현식과 문

리터럴과 표현식 / 표현식이 아닌 문 / 세미콜론 자동 삽입 / 데이터 타입 / 연산자 / 제어문

2022-01-13에 씀

본 시리즈는 모던 자바스크립트 Deep Dive 책을 참고하여 작성하고 있습니다.

표현식과 문

내용을 시작하기 전에, 몇 가지 용어에 대한 정의를 내리려고 한다.

값은 어떤 식이 평가되어 생성된 결과이다. 예를 들어, 10 + 20 이라는 식은 평가되어 숫자 값 30을 생성한다. 모든 값은 데이터 타입을 가지고, 메모리에 2진수의 나열로 저장된다.

값을 생성하는 방법에는 여러가지가 있다.

  1. 식으로 생성하기 (ex. 10 + 20 )
  2. 표현식으로 생성하기 (ex. sum !== 10 )
  3. 리터럴로 생성하기 (ex. 'hello' )
  4. ...

리터럴

리터럴은 사람이 이해 가능한 문자나 약속된 기호를 사용해 값을 생성하는 표기법이다. 사람이 이해할 수 있는 문자나 기호에는 아라비아 숫자, 알파벳, 한글, ‘’, “”, ., [], \{} 등이 있다. 자바스크립트 엔진은 코드가 실행되는 시점인 런타임에 리터럴을 평가해 값을 생성한다.

정수 리터럴100
부동소수점 리터럴10.5
2진수 리터럴0b010001
문자열 리터럴"hello”
불리언 리터럴true
null 리터럴null
객체 리터럴{name: ‘Lee’, address: ‘Seoul’}
배열 리터럴[ 1, 2, 3 ]
함수 리터럴function() {}

표현식

표현식은 값으로 평가될 수 있는 문이다. 다시 말해, 값으로 평가될 수 있는 문이면 모두 표현식이다. 표현식이 평가되면 새로운 값이 생성되거나 기존의 값을 참조한다. 리터럴도 값으로 평가되는 문이므로, 표현식이다. 이때, 표현식과 표현식이 평가된 값은 동치이다. 따라서 값이 위치할 수 있는 자리에는 표현식도 위치할 수 있고, 표현식이 다른 표현식의 일부가 되어 새로운 값을 만들 수도 있다.

1 100; // 리터럴 표현식
2 50 + 50; // 연산자 표현식
3
4 var score; // *-- 변수 선언문은 값으로 평가될 수 없으므로 표현식이 아니다
5
6 score = 50 + 50; // 할당문 (표현식)
7 score // 식별자 표현식
8
9 result = score * 500;
10 // 식별자 표현식 score는 100으로 평가됨
11 // result -> 50000

세미콜론과 세미콜론 자동 삽입 기능(ASI)

세미콜론(;)은 문의 종료를 의미하고, 자바스크립트 엔진은 세미콜론을 기준으로 문이 종료되는 위치를 파악한다. 예외로 코드 블록 \{ ... } 은 그 자체로 종결성을 가지기 때문에 세미콜론을 붙이지 않는다. 그런데 자바스크립트에서 세미콜론을 붙이는 것은 옵션이고, 생략 가능하다. 자바스크립트 엔진은 소스코드를 해석하면서 문의 끝이라고 예측되는 곳에 세미콜론을 자동으로 붙여준다. 이 기능을 세미콜론 자동 삽입 기능(Automatic Semicolon Insertion)이라고 한다. 그러나 ASI가 개발자가 의도한 대로 동작하는 경우가 있다.

1 var bar = function () \{}
2 (function() \{})();
3 // ASI 동작 결과 => var bar = function () \{}(function() \{})();
4 // 예측 => var bar = function() \{}; (function() \{})();

이러한 문제를 방지하기 위해 세미콜론을 붙여야 한다는 주장이 다수이다.

표현식인 문과 표현식이 아닌 문

표현식은 문 그 자체가 될 수도 있고, 문의 일부가 될 수도 있다.

1 // var x; 처럼 변수 선언문은 표현식이 아니다
2 // x = 2; 처럼 할당문은 값으로 평가될 수 있으므로 표현식이다
3 // 따라서 아래 문의 일부만 표현식이다
4 var x = 2;
5
6 // 할당문은 표현식이다. 아래 문은 문이면서 표현식이다
7 x = 3 + 5;

표현식인 문과 표현식이 아닌 문을 구분하는 방법은 변수에 할당해 보는 것이다. 표현식은 값으로 평가될 수 있는 문이고, 변수는 하나의 을 저장하기 위해 확보한 메모리 공간이기 때문에, 어떤 문을 변수에 할당할 수 있다는 것은 그 문의 평가 결과가 이라는 것이고, 즉 그 문은 표현식이다.

1 var foo = var x; // Uncaught SyntaxError: Unexpected token 'var'
2 var bar = (x = 3 + 5);
3 console.log(var) // 8

크롬 개발자 도구에서는 표현식이 아닌 문을 실행하면 언제나 undefined를 출력하고, 이를 완료 값이라고 한다. 완료 값은 평가 결과가 아니므로 이 값을 변수에 할당하거나 참조할 수 없다. 표현식인 문을 실행하면 그 식이 평가된 결과를 반환한다.

연산자

산술 연산자

1 // 이항 산술 연산자 - 피연산자가 2개 이상
2 5 + 2
3 5 - 2
4 5 * 2
5 5 / 2
6 5 % 2
7
8 // 단항 산술 연산자 - 피연산자가 1개
9 var x = 1;
10
11 // -- 부수 효과가 있는 단항 산술 연산자
12 x++; // 선할당 후증가
13 x--; // 선할당 후감소
14 ++x; // 선증가 후할당
15 --x; // 선감소 후할당
16
17 // -- 부수 효과가 없는 단항 산술 연산자
18 +x; // 피연산자를 숫자 타입으로 변환
19 -x; // 양수를 음수로, 음수를 양수로 반전하여 반환

문자열 연결 연산자: 피연산자 중 하나 이상이 문자열인 경우, 문자열로 연결되어 반환된다.

1 '1' + 2; // '12'
2 1 + '2'; // '12'
3
4 // 암묵적 타입 변환 - 타입 강제 변환
5 1 + true // 2
6 1 + false // 1
7 1 + null // 1
8 +undefined // NaN
9 1 + undefined // NaN

할당 연산자

1 var x;
2
3 x = 10;
4 x += 5;
5 x -= 5;
6 x *= 5;
7 x /= 5;
8 x %= 5;
9 x += 'hi'; // '0hi'

비교 연산자

동등/일치 비교

1 var x = 2;
2 var y = 3;
3
4 x == y; // x와 y의 값이 같은가?
5 x === y; // x와 y의 값과 타입이 같은가?
6 x != y; // x와 y의 값이 다른가?
7 x !== y; // x와 y의 값과 타입이 다른가?

값만 비교할 때는 암묵적으로 타입이 자동 변환으로 타입을 일치시킨 후에 같은 값인지 비교한다. 이를 **동등 비교(==)**라고 한다. 따라서 5 == '5' 는 참이고, 5 === '5' 는 거짓이다. 그런데 동등 비교는 결과를 예측하기 어렵기 때문에 사용하지 않는 편이 좋다.

1 '0' == '' // false
2 0 == '' // true
3 0 == '0' // true
4 0 == '0' // true
5 false == 'false' // false
6 false == '0' // true
7 false == null // false
8 false == undefined // false

일치 비교(===) 연산자는 좌항과 우항이 타입도 같고 값도 같아야 true를 반환한다. 그리고 암묵적 타입 변환을 하지 않는다.

1 5 === 5; // true
2 5 === '5'; // false

동등 비교 연산자 주의

1 NaN === NaN; // false
2 // --- NaN 검사를 위해서는 isNaN()을 사용해야 한다
3
4 ****0 === -0 // true
5 0 == -0 // true
6
7 -0 === +0; // true
8 Object.is(-0, +0); // false
9
10 NaN === NaN;
11 Object.is(NaN, NaN); // true

대소 관계 비교

1 5 > 0 // true
2 5 > 5 // false
3 5 >= 5 // true
4 5 <= 5 // true

삼항 조건 연산자

1 var x = 2;
2
3 console.log(x % 2 ? '홀수' : '짝수')

논리 연산자

우항과 좌항의 피연산자를 논리 연산

1 true || true // true
2 true || false // true
3 false || true // true
4 false || false // false
5
6 true && true // true
7 true && false // false
8 false && true // false
9 false && false // false
10
11 'cat' && 'dog' // 'dog' -- 모두 참일 때, 마지막 피연산자를 반환
12 'cat' || 'dog' // 'cat' -- 좌항부터 확인해서 처음으로 참인 피연산자를 반환

쉼표 연산자

1 var x, y, z;
2 x = 1, y = 2, z = 3; // 3

그룹 연산자

1 10 * (2 + 3) // 50
2 10 * 2 + 3 // 23

typeof 연산자

지수 연산자

(ES7 도입)

1 2 ** 3 // 8
2 2 ** 2.5 // 2^(2.5) => 5.6568...
3 2 ** 0 // 1
4 2 ** -2 // 2^(-2) => 1/4 => 0.25
5
6 Math.pow(2, 3); // 지수 연산자 도입되기 전에 사용, 8 반환

그 외 연산자

1 var object = \{ name: "yj", number: 33, dog: \{ name: "jj" } }
2 var object_2 = \{ number: 34 }
3
4 // 옵셔널 체이닝 연산자
5 console.log(object_2.dog?.name) // "yj"
6 console.log(object_2.dog.name) // Uncaught TypeError: Cannot read properties of undefined (reading 'name')
7
8 // null 병합 연산자
9 console.log(object_2.dog); // undefined
10 console.log(object_2.dog ?? \{ name: "kk" }); // \{ name: "kk" }
11
12 // 프로퍼티 삭제 연산자
13 console.log(object); // \{ name: "yj", number: 33, dog: \{ name: "jj" } }\
14 delete object.number;
15 console.log(object); // \{name: 'yj', dog: \{…}}
16
17 // new 연산자
18 var fruits = new Array('사과', '바나나')
19 console.log(fruits); // ['사과', '바나나']
20
21 // 좌변 객체가 우변 생성자 함수와 연결된 인스턴스인지 판별
22 console.log(fruits instanceof Array) // true
23
24 // 프로퍼티 존재 확인
25 console.log('name' in object) // true
26 console.log('name' in object_2) // false

제어문

블록문

1 \{
2 var foo = 1;
3 foo++;
4 }
5
6 if (true) \{
7 console.log("hi");
8 }

조건문

if ... else

1 if (조건식1) \{
2 // 조건식 1이 true
3 } else if (조건식2) \{
4 // 조건식 2가 true
5 } else \{
6 // 모두 false
7 }

삼항연산자로 바꿔보기

1 var num = 2;
2
3 if (num > 0) \{
4 console.log("양수");
5 } else \{
6 console.log("양수가 아님");
7 }
8
9 num > 0 ? console.log("양수") : console.log("양수가 아님");

switch

1 switch (표현식) \{
2 case 표현식1:
3 // 표현식 == 표현식1
4 break;
5 case 표현식2:
6 // 표현식 == 표현식2
7 break;
8 default:
9 // 해당되는 case 없을 경우 실행
10 }
1
2 var year = 2022;
3 var month = 2;
4 var days;
5
6 switch (month) \{
7 case 1: case 3: case 5: case 7: case 8: case 10: case 12:
8 days = 31;
9 break;
10 case 4: case 6: case 9: case 11:
11 days = 30;
12 break;
13 case 2:
14 days = getFebDays(year);
15 break;
16 default:
17 console.log('Invalid month');
18 }
19
20 console.log("days");

반복문

for 문

1 for (변수 선언문 혹은 할당문; 조건식; 증감식) \{
2 ...
3 }
4
5 for (var i = 1; i >= 0; i--) \{
6 console.log(i);
7 }
8
9 for (;;) \{
10 // 무한루프
11 }

while 문

1 var count = 0;
2
3 while (count < 3) \{
4 console.log(count);
5 count++;
6 }
7
8 while (true) \{
9 // 무한루프
10 }

do...while 문

1 var count = 0;
2
3 // 코드 블록 한 번 실행 후, count가 3보다 작을 때까지 코드 블록 계속 반복 실행
4 do \{
5 console.log(count);
6 count++;
7 } while (count < 3);

break 문

레이블 문

1foo: \{
2 console.log(1);
3 break foo;
4 console.log(2);
5}
6
7console.log('hi');
8
9// 실행 결과: 1, hi

continue 문

1for (var i = 0; i < 10; i++) \{
2 if (i < 5) continue;
3 console.log(i);
4}
5// 5 6 7 8 9
프로필 사진

조예진

이전 포스트
JS01 - 변수
다음 포스트
객체지향 프로그래밍이란?