표현식(Expression) 과 문장(Statement)

y0ung·2020년 12월 7일
0

자바스크립트에는 대표적으로 2가지 문법적 카테고리가 있다

1. Statements(문장)
2. Expressions(표현식)

표현식은 문장처럼 동작할수 있기 때문에 이 둘을 구분하는 것은 중요하다. 그리고 이것이 포현문이 존재하는 이유이기도 하다. 하지만 반대로 문장은 표현식처럼 동작 할수 없다.

표현식(Expressions)

값을 만들어내는 표현식

표현식은 값 하나로 값을 도출해 내는 자바스크립트 코드 조각(snippet) 이다. 표현식은 우리가 원하는 만큼 길어질 수 있지만 언제나 동일한 값이 나오진 않는다.

2 + 2 * 3 / 2

(Math.random() * (100-20)) + 20

functionCall()

window.history ? useHistory() : noHistoryFallback()

1+1, 2+2, 3+3

declaredVariable

true && functionCall()

true && declaredVariable

위의 모든 코드는 포현식이다. 그리고 표현식은 자바스크립트 코드 중 값이 들어가는 곳이면 어디에나 넣을수 있다. console.log의 인자는 콘솔이 로깅될 때 하나의 값으로 변한다.

console.log(true && 2 * 9) // 18

표현식은 반드시 상태(state)를 바꿀 필요는 없다.

const assignedVariable = 2; // 이건 문장(Statement)입니다. assignedVariable은 상태입니다.

assignedVariable * 4 // 표현식(Expression)입니다.

assignedVariable * 10 // 표현식(Expression)입니다.

assignedVariable - 10 // 표현식(Expression)입니다.

console.log(assignedVariable) // 2

assignmedVariable에 표현식으로 값을 변경하여도 결과값은 여전히 2이다.
함수 호출은 포현식이다.하지만 함수는 값을 변화시키는 문장을 포함할 수 있다. foo()함수는 undefined나 어떤 다른 값을 반환할 수 있는 표현식이다. 하지만 만일 foo가 다음과 같이 작성됐다면,

const foo = foo() => {
  assignedValue = 14
}

그땐, foo를 호출하는 것은 표현식 일지라도, 함수를 호출하면 결국 state가 바뀌게 된다. 그래서 foo 함수를 더 나은 방법으로 재 작성하려면 문장은 다음과 같을 것이다.

const foo = foo() => {
  return 14;
}

assignedVariable = foo()

아니면 더 나은 방법으로

const foo = foo (n) => {
  return n
}

assignedVariable = foo(14)

이 편이 가독성이 더 좋다. 또한 어딘가에 끼워넣기 좋으며 표현식과 문장 사이에서 확연히 구분된다. 이것이 선언적이고 함수적인 자바스크립트의 기반이다.

문장(Statement)

함수형 프로그래밍의 관점에서 문장은 골칫거리 이다. 기본적으로 문장은 무언가를 수행하는데,
자바스크립트에서 문장은 값이 들어와야 할 곳에 들어 갈수 없다. 그래서 그들은 함수의 인자로도, 대입 연산의 값으로도, 연산자의 피연산자로도 사용될 수 없다.

자바 스크립트의 문장의 종류

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

브라우저의 콘솔에 다음과 같은 코드를 치고 엔터를 치면 어떻게 될까?

if(true) {9 + 9}

18이 리턴된다. 하지만 이 결과를 표현식 처럼 사용하거나 자바스크립트 코드 내에 값이 들어갈 어딘가에 넣을수는 없다. 문장이 아무것도 반환하지 않을 것이라 예생해서 반환된 값을 이용할수 없으면 문장이 값을 반환하는 것은 아무런 의미가 없다.

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

함수 선언은 문장이다.

function foo (func) {
  return func.name;
}

함수 표현식

함수 표현식은 표현식이다. 익명 함수라 불리우는 것들이다.

console.log(foo(function () {})); // ""

네임드 함수 표현식

이름이 있는 표현식이다.

console.log(foo(function myName () {} )); // "myName"

자바스크립트에서 값이 들어올 곳에 함수를 선언할 때마다, 자바스크립트는 그것을 값으로 다루려 한다. 만일 그 함수가 값으로 사용될 수 없다면, 에러가 발생한다.
반면에 스크립트, 모듈 블록 문장(자바스크립트에서 값이 들어가는 곳이 아닌 위치에 있는)의 전역 단계(Global level)에 함수를 선언하는 것은 결과적으로 함수 선언이다.

if () {
  function foo () {} // 블록의 가장 상위 레벨, 함수 선언
}

function foo () {} // 전역 레벨, 함수 선언

function foo () {
  function bar () {} // 블록의 가장 상위 레벨, 함수 선언
}

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

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

function foo () {
  return function bar () {
    function baz () {} // 블록의 가장 상위 레벨, 함수 선언  
  }
}

function () {} // 문법 에러: 함수 문장(statement)은 이름이 필요합니다.

표현식을 문장으로 바꾸기: 표현식 문장(Expression Statements)

2 + 2; // expression statement
foo(); // expression statement

표현식을 표현식 문장으로 바꿀수 있다. 뒤에 세미 콜론만 추가하면 되는데, 2 + 2자체는 표현식 이다. 하지만 그 줄 자체는 표현식 문장이다.

2+2 // 그 자체로 표현식

foo(2+2) // 그래서 어디든 값이 들어가야 할 곳에서 사용가능

true ? 2+2 : 1 + 1

function foo () {return 2+2}

2+2; // 표현식 문장(Expression Statements)
foo(2+2;) // 문법 에러(Syntax Error)

세미콜론 vs 콤마 연산자

세미콜론을 붙이면, 여러 줄의 문장을 하나의 줄에 넣을 수 있다.

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

콤마 연산자는 여러개의 표현식을 연결할 수 있도록한다. 반환은 마지막 표현식만 반환하다.

console.log( (1+2, 3, 4) ) // 4

console.log( (2, 9/3, function () {}) ) //function () {}

console.log( (3, true ? 2+2 : 1+1) ) // 4

자바스크립트 엔젠에게 값을 전달할 때, 값이 들어가야 할 곳에 괄호 () 를 통해 값을 전달한다. 괄호가 없으면 각각을 console.log 의 인자로 보낸다.

function foo() {return 1,2,3,4}
foo() // 4

모든 표현식은 왼쪽에서 오른쪽으로 계산하기때문에 마지막 것이 리턴된다.

IIFEs (Immediately Invoked Function Expression(즉시 호출되는 함수 표현식))

익명함수는 표현식으로 쓸수 있다. 자바스크립트에서 값이 들어갈 곳에 쓰일 수 있거나, 자바스크립트에서 값이 들어갈 곳에 괄호를 쓸수 있다면 익명 함수를 값으로 넘길 수 있다는 것을 의미한다.

function () {}

위의 코드는 유효하지 않은 반면에 아래의 코드는 유효하다.

(function () {}) // function () {} 을 리턴한다.

만약 익명 함수를 괄호 속에 넣는다면 즉시 같은 익명 함수를 리턴한다.

(function () {
  // do something
})()

그래서 다음과 같은 것도 가능하다

(function () {
  console.log("익명함수 즉시 호출");
})() // "익명함수 즉시 호출"

(function () {
  return 3;
})() // 3

console.log((function () {
  return 3;
})()) // 3


// 인자를 넘길 수도 있습니다.
(function (a) {
  return a;
})("저는 인자입니다."); // 저는 인자입니다.

오브젝트 리터럴 vs 블록 문장(Block Statement)

리터럴 : 값 그 자체

r: 2+2 // 유효함

foo()

cnost foo = () => {}

글로벌 스코프에 위치한 위의 문장들은 유효한 자바스크립트로 변경되어 실행한다. r은 label이라 불리는 것이다. breaking loops를 구성할 때 유용하다.

loop: {
  for(const i = 0; i < 2; i++) {
    for(const n = 0; n < 2; n++){
      break loop; // 바깥루프를 중단하여 전체 루프를 중단한다.
    }
  }
}

라벨을 어떤 표현식이나 표현식 문장에 붙일수 있다.

lab: function a () {}
console.log(lab) // ReferenceError: lab is not defined

{}와 같은 중괄호는 문장과 표현식 문장들을 그룹화하는데 도움을 준다.

{let a = "b"; func(); 2+2} // 4

위의 내용을 각각 콘솔에 넣어 붙여넣기 하면 4를 반환한다. 그리고 console.log(a);는 b를 반환한다. 이것을 블록 문장이라 부른다.

console.log({a: 'b'}); // {a: 'b'} 오브젝트 리터럴

console.log({var a = "b", func(), 2+2}) // SyntaxError 블록 문장

const obj = {var a = "b", func(), 2+2} // SyntaxError 블록 문장

블록 문장을 값이나 표현식으로 사용할 수 없다. console.log문장을 인자로 받아들일수 없기 때문이다.

{} + 1 // 1

{2} + 2 // 2

{2+2} + 3 // 3

{2+2} - 3 // -3

위의 코드들이 문법 에러가 나거나 1,4,7,1과 같은 숫자를 각각 리턴할수 있다고 예측할수 있다. 하지만 문장은 어느것도 반환하도록 되어있지 않다. 왜냐하면 값으로 쓰일수 없기 때문이다. 그래서 자바스크립트는 error를 내보내지 않는 대신에 + 연산자의 피연산자를 숫자나 문자열로 바꾼다. 만일 바꿀 수 없는 값이라면 에러를 내보낸다. 블록 문장에서 무엇이 반환되던지 그것은 암묵적으로 0로 강제 형변환되어 피연산자로 사용된다.


참고

profile
어제보다는 오늘 더 나은

0개의 댓글