식(expression) vs 문(statement)

33-js-concepts를 스터디하며 정리한 포스트 입니다.

자바스크립트의 식과 문

자바스크립트에는 대표적은 2가지 문법 카테고리가 존재한다.

  1. (Expression)
  2. (Statement)

(Expression)은 (Statement)처럼 동작할 수 있다.
(Statement)은 (Expression)처럼 동작할 수 없다.

식 (Expression)

(Expression)은 하나의 을 만들어 낸다.
(Expression)의 종류는 4가지가 존재한다.

아래 같은 number, string, undefined, null같은 값 자체도 식이다.

1
3.14
;('expression')
undefined
null
  • 연산식

아래와 같은 연산자가 포함된 경우도 결과적으로 하나의 값을 갖는 식이 된다.
연산자가 많아져 연산식이 길어져도 결국은 하나의 값만 갖는다.

1 + 1 // 2
2 >
  1(
    // true
    1 * 2 * 3
  ) /
    2 // 3
1 * 2 * 3 > 1 // true
true || 1 // true
5 > 1 ? 5 : 1 // 5
  • 리터럴

객체를 생성하기 위한 리터럴 또한 참조값을 갖는 식이다.

{} // 객체 리터럴
function () {} // 함수 리터럴
[] // 배열 리터럴
/\b/ // 정규식 리터럴
  • 함수 호출

아래와 같이 함수의 호출 결과 또한 값을 반환하는 식이다.
voidFunction과 같이 반환값이 없는 함수도 undefined라는 값을 반환하는 이된다.

Math.ceil(3.14) // 4
voidFunction()(
  // undefined
  5
).toString() // "5"

문 (Statement)

(Statement)은 기본적으로 무언가를 수행한다.
자바스크립트에서 문장은 값이 들어가야할 곳에 들어갈 수 없다.
따라서 (Statement)은 함수의 인자, 대입 연산의 값, 연산자의 피연산자로 사용할 수 없다.

foo(if (true) { return 2 }) // Uncaught SyntaxError: Unexpected token 'if'

자바스크립트의 문은 아래와 같다.

  1. if
  2. if-else
  3. while
  4. do-while
  5. for
  6. switch
  7. for-in
  8. debugger
  9. variable declaration

아래와 같은 코드를 콘솔에 치면 18이 반환된 것을 볼 수 있다.

if (true) { 9 + 9 }
1

하지만 우리는 이 (Statement)의 결과를 (Expression)처럼 사용할 수 없다.
지금까지의 내용으로는 (Statement)은 아무 값도 반환하지 않았어야 한다.
우리는 이 반환된 값을 이용할 수 없고, 따라서 (Statement)이 반환하는 값은 의미가 없다.

That’s Javascript for you, weird.
자바스크립트가 널 위한 거야, 이상해.

함수 선언, 함수 표현식, 네임드 (Named) 함수 표현식

기본적으로 함수의 선언은 (Statement)이다.

function thisIsStatement(func) {
  return func.name
}

익명 함수라 불리는 함수 표현식 (Expression)이다.

thisIsStatement(function() {}) // ""

익명 함수와 비슷하지만 이름이 있는 네임드 함수 표현식 (Expression)이다.

thisIsStatement(function namedFunc() {}) // "namedFunc"

값이 들어올 곳함수를 선언하면, 자바스크립트는 그 함수를 값으로 사용하려 한다.
값이 들어올 곳에 선언된 함수가 값으로 사용될 수 없다면, 에러가 발생할 것이다.
값이 들어가는 곳이 아닌 위치에 있는 곳에 함수를 선언하면 (Statement)으로 쓰인다.

if () {
    function foo() {} // 함수 선언문
}

function foo() {} // 함수 선언문

function foo() {
    function bar() {} // 함수 선언문
}

function foo() {
    return function bar() {} // 네임드 함수 표현식
}

callFunction(function () {}) // 익명 함수 표현식

function foo() {
    return function bar() {
        function baz() {} // 함수 선언문
    }
}

function () {} // Function statements require a function name

식 (Expression)은 상태 (State)를 바꿀 수 있다.

아래의 코드에서 변수 a는 상태다.
a * 1, a - 1같은 식에 의해서도 a의 값은 그대로 1이다.

let a = 1 // 문 (Statement)

a * 1 // 식 (Expression)
a - 1 // 식 (Expression)
a + 1 // 식 (Expression)
a / 1 // 식 (Expression)

console.log(a) // 1

이 예제는 사용한 이 변수가 가진 상태 (State)를 변경시키지 않았다.
하지만 함수 호출과 같은 식은 상태을 변화시키는 문을 포함할 수 있다.

let a = 1
function foo() {
  a = 2
}

console.log(a) // 1
foo()
console.log(a) // 2

foo함수를 호출하는 것은 이지만 내부에 상태를 변화시키는 문이 존재한다.
함수 호출 식으로 인한 의도치않은 상태 변경을 막기위해서는 명시적 반환을 하면 된다.

let a = 1
function foo() {
  return 2
}

console.log(a) // 1
foo()
console.log(a) // 1
a = foo()
console.log(a) // 2

식 (Expression)을 문 (Statement)으로 바꾸기

(Expression)을 (Statement)으로 바꾸기 위해서는 ;만 추가하면 된다.

2 + 2
2 + 2

위의 2 + 2 (Expression)이다.
식에 ;을 추가한 2 + 2;표현식 문장 (Expression Statements)이다.
2 + 2; (Statement)이 되었기 떄문에 아래와 같이 사용하면 에러가 발생한다.

Math.ceil(2 + 2;) // ERROR

(Expression)이 들어가야할 곳에 (Statement)이 들어갔기 때문이다.

세미콜론과 콤마 연산자

세미콜론 (;)을 사용하면 여러줄의 (Statement)을 한 줄에 넣을 수 있다.

let a
function foo() {}
const b = 2

콤마 연산자(,)를 사용하면 여러 개의 (Expression)을 연결할 수 있다.
반환되는 식의 값은 마지막 식값만 반환된다.

;(1 + 2, true, Math.ceil(4.5))(
  // 5
  function() {},
  5 > 1 ? 5 : 1
) // 5

또한 (Expression)은 왼쪽에서 오른쪽으로 계산된다.

function foo() {
  return true, false, 1, 'expression'
}
foo() // expression

Written by@Minsu Kim
Software Engineer at KakaoPay Corp.