JS02 - 표현식과 문

조예진·2022년 1월 13일
0

JavaScript 스터디

목록 보기
3/16
post-thumbnail

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

표현식과 문

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

  • : 표현식이 평가되어 생성된 결과
  • 리터럴: 사람이 이해 가능한 문자나 기호를 사용해 값을 생성하는 표기법
  • 표현식: 값으로 평가될 수 있는 문
  • 평가(evaluation): 식을 해석해서 값을 생성하거나 참조하는 것
  • 문(statement)
    • 프로그램을 구성하는 기본 단위이자 최소 실행 단위
    • 명령문이라고도 부름 (컴퓨터에 내리는 명령)
    • 문이 실행되면 명령이 실행됨
    • 선언문, 할당문, 조건문, 반복문 등

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

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

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

리터럴

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

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

표현식

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

100; // 리터럴 표현식
50 + 50; // 연산자 표현식

var score; // *-- 변수 선언문은 값으로 평가될 수 없으므로 표현식이 아니다

score = 50 + 50; // 할당문 (표현식)
score // 식별자 표현식

result = score * 500; 
// 식별자 표현식 score는 100으로 평가됨
// result -> 50000

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

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

var bar = function () {}
(function() {})();
// ASI 동작 결과 => var bar = function () {}(function() {})();
// 예측 => var bar = function() {}; (function() {})();

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

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

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

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

// 할당문은 표현식이다. 아래 문은 문이면서 표현식이다
x = 3 + 5;

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

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

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

연산자

  • 연산자(operator): 하나 이상의 표현식을 대상으로 산술, 할당, 비교, 논리, 타입, 지수 연산 등을 수행해 하나의 값을 만듦
  • 피연산자(operand): 연산의 대상, 값으로 평가될 수 있는 표현식이어야 함
  • 연산자 표현식: 피연산자와 연산자의 조합으로 이루어진 식. 평가될 수 있는 표현식이다.

산술 연산자

  • 피연산자를 대상으로 수학적 계산 수행 후 새로운 숫자 값 생성
  • 산술 연산 불가능할 경우 NaN 값을 반환한다.
// 이항 산술 연산자 - 피연산자가 2개 이상
5 + 2
5 - 2
5 * 2
5 / 2
5 % 2

// 단항 산술 연산자 - 피연산자가 1개
var x = 1;

// -- 부수 효과가 있는 단항 산술 연산자
x++; // 선할당 후증가
x--; // 선할당 후감소
++x; // 선증가 후할당
--x; // 선감소 후할당

// -- 부수 효과가 없는 단항 산술 연산자
+x; // 피연산자를 숫자 타입으로 변환
-x; // 양수를 음수로, 음수를 양수로 반전하여 반환

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

'1' + 2; // '12'
1 + '2'; // '12'

// 암묵적 타입 변환 - 타입 강제 변환
1 + true // 2
1 + false // 1
1 + null // 1
+undefined // NaN
1 + undefined // NaN

할당 연산자

  • 우항에 있는 피연산자의 평가 결과를 좌항에 있는 변수에 할당
  • 할당문은 할당된 값으로 평가된다.
var x;

x = 10;
x += 5;
x -= 5;
x *= 5;
x /= 5;
x %= 5;
x += 'hi'; // '0hi'

비교 연산자

  • 좌항, 우항 비교 후 그 결과를 불리언 값으로 반환

동등/일치 비교

var x = 2;
var y = 3;

x == y; // x와 y의 값이 같은가?
x === y; // x와 y의 값과 타입이 같은가?
x != y; // x와 y의 값이 다른가?
x !== y; // x와 y의 값과 타입이 다른가?

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

'0' == '' // false
0 == '' // true
0 == '0' // true
0 == '0' // true
false == 'false' // false
false == '0' // true
false == null // false
false == undefined // false

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

5 === 5; // true
5 === '5'; // false

동등 비교 연산자 주의

NaN === NaN; // false
// --- NaN 검사를 위해서는 isNaN()을 사용해야 한다

****0 === -0 // true
0 == -0 // true

-0 === +0; // true
Object.is(-0, +0); // false

NaN === NaN;
Object.is(NaN, NaN); // true

대소 관계 비교

5 > 0 // true
5 > 5 // false
5 >= 5 // true
5 <= 5 // true

삼항 조건 연산자

  • 조건식 ? 조건식이 true일 때 반환할 값 : 조건식이 false일 때 반환할 값
  • 삼한 조건 연산자가 사용된 문은 평가되어 두 번째 피연산자나 세 번째 피연산자를 반환 ⇒ 값처럼 사용
    • 삼항 조건 연산자 표현식은 값으로 평가할 수 있는 표현식인 문
var x = 2;

console.log(x % 2 ? '홀수' : '짝수')

논리 연산자

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

  • ||: 논리합 (OR)
  • &&: 논리곱 (AND)
  • !: 부정 (NOT) ⇒ 우항의 피연산자를 논리 연산
true || true // true
true || false // true
false || true // true
false || false // false

true && true // true
true && false // false
false && true // false
false && false // false

'cat' && 'dog' // 'dog' -- 모두 참일 때, 마지막 피연산자를 반환
'cat' || 'dog' // 'cat' -- 좌항부터 확인해서 처음으로 참인 피연산자를 반환
  • ! 연산자는 언제나 불리언 값을 반환한다. 피연산자는 불리언 값일 필요는 없다. 암묵적 타입 변환을 수행한다.
  • ||, && 연산자의 평가 결과는 불리언이 아닐 수 있다.

쉼표 연산자

  • 왼쪽 피연산자부터 차례대로 피연산자를 평가
  • 마지막 피연산자의 평가 결과를 반환
var x, y, z;
x = 1, y = 2, z = 3; // 3

그룹 연산자

  • 소괄호로 피연산자를 묶는다. 연산자 우선순위가 가장 높기 때문에, 감싸고 있는 피연산자들을 가장 먼저 평가한다.
10 * (2 + 3) // 50
10 * 2 + 3 // 23

typeof 연산자

  • 피연산자 데이터 타입을 문자열로 반환
    • string, number, boolean, undefined, symbol, object, function 중 하나를 반환
      • typeof null // => object

지수 연산자

(ES7 도입)

2 ** 3 // 8
2 ** 2.5 // 2^(2.5) => 5.6568...
2 ** 0 // 1
2 ** -2 // 2^(-2) => 1/4 => 0.25

Math.pow(2, 3); // 지수 연산자 도입되기 전에 사용, 8 반환

그 외 연산자

var object = { name: "yj", number: 33, dog: { name: "jj" } }
var object_2 = { number: 34 }

// 옵셔널 체이닝 연산자
console.log(object_2.dog?.name) // "yj"
console.log(object_2.dog.name) // Uncaught TypeError: Cannot read properties of undefined (reading 'name')

// null 병합 연산자
console.log(object_2.dog); // undefined
console.log(object_2.dog ?? { name: "kk" }); // { name: "kk" }

// 프로퍼티 삭제 연산자
console.log(object); // { name: "yj", number: 33, dog: { name: "jj" } }\
delete object.number;
console.log(object); // {name: 'yj', dog: {…}}

// new 연산자
var fruits = new Array('사과', '바나나')
console.log(fruits); // ['사과', '바나나']

// 좌변 객체가 우변 생성자 함수와 연결된 인스턴스인지 판별
console.log(fruits instanceof Array) // true

// 프로퍼티 존재 확인
console.log('name' in object) // true
console.log('name' in object_2) // false

제어문

블록문

  • == 코드 블록 == 블록
  • 0개 이상의 문을 중괄호로 묶은 것
{
  var foo = 1;
  foo++;
}

if (true) {
  console.log("hi");
}

조건문

  • 조건식의 평가 결과에 따라 코드 블록의 실행을 결정
  • 조건식: 불리언 값으로 평가될 수 있는 표현식

if ... else

if (조건식1) {
  // 조건식 1이 true
} else if (조건식2) {
  // 조건식 2가 true
} else {
 // 모두 false
}

삼항연산자로 바꿔보기

var num = 2;

if (num > 0) {
  console.log("양수");
} else {
  console.log("양수가 아님");
}

num > 0 ? console.log("양수") : console.log("양수가 아님");

switch

switch (표현식) {
  case 표현식1:
    // 표현식 == 표현식1
    break;
  case 표현식2:
    // 표현식 == 표현식2
    break;
  default:
    // 해당되는 case 없을 경우 실행
}
  • 폴스루(fall through): 표현식 평가 결과와 일치하는 case 문으로 이동해 문을 실행했지만, switch 문을 탈출하지 않고 이후 모든 case와 default 문 실행 → break 문을 사용해야 함

var year = 2022;
var month = 2;
var days;

switch (month) {
  case 1: case 3: case 5: case 7: case 8: case 10: case 12:
    days = 31;
    break;
  case 4: case 6: case 9: case 11:
    days = 30;
    break;
  case 2:
    days = getFebDays(year);
    break;
  default:
    console.log('Invalid month');
}

console.log("days");

반복문

  • 조건식 평가 결과가 참인 경우 코드 블록 실행
  • 실행 후 조건식 다시 평가 후 참이면 다시 코드 블록 실행

for 문

for (변수 선언문 혹은 할당문; 조건식; 증감식) {
  ...
}

for (var i = 1; i >= 0; i--) {
  console.log(i);
}

for (;;) {
  // 무한루프
}

while 문

var count = 0;

while (count < 3) {
  console.log(count);
  count++;
}

while (true) { 
  // 무한루프
}

do...while 문

var count = 0;

// 코드 블록 한 번 실행 후, count가 3보다 작을 때까지 코드 블록 계속 반복 실행
do {
  console.log(count);
  count++;
} while (count < 3);

break 문

  • 레이블 문, 반복문, switch 문의 코드 블록을 탈출한다.
  • 그 외의 블록에서 break 문을 사용하면 SyntaxError

레이블 문

foo: {
  console.log(1);
  break foo;
  console.log(2);
}

console.log('hi');

// 실행 결과: 1, hi

continue 문

  • 반복문 코드 블록 실행을 현 시점에서 중단
  • 반복문 증감식으로 실행 흐름 이동
for (var i = 0; i < 10; i++) {
  if (i < 5) continue;
  console.log(i);
}
// 5 6 7 8 9
profile
https://oooooroblog.com 으로 이사갔어요

0개의 댓글