티스토리 뷰

Javascript

#7 문장 vs 표현식

심재철 2019. 7. 29. 10:47

https://www.notion.so/simsimjae/7-vs-3a7d713eda67435b8da4b6d3d453170c

이 코드를 정확히 설명할 수 있다면 이 페이지에서 나가도 좋다.

{} + 1 // 1 {2} + 2 // 2 {2+2} + 3 // 3 {2+2} - 3 // -3


const foo = () => { value = 14; } console.log(foo()); //undefined

  • 위와 같이 함수 호출 자체는 어떤 값(undefined)이 리턴되기 떄문에 표현식이라고 할 수 있다.

  • 마찬가지로, 함수 내부에서는 어떤 변수의 상태를 바꿀수 있는 문장을 포함한다.

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

  • 그러므로 저렇게 애매하게 표현식도, 문장도 아니게 함수를 만들지 말고 위 처럼 코드를 변형해서 사용하는것이 좀 더 명확한(표현식으로써의 함수) 코드 작성법이다.

    foo( if() { return 2; } ); //error

  • 이처럼 값이 와야 할곳에 문장을 넣게되면 에러가 발생한다. 이곳엔 표현식이 들어가야한다.

    if (true) {9+9} //18

  • 위와 같은 문장은 18을 리턴한다.

  • 위 문장은 statement인데 마치 자기가 expression인것마냥 값을 리턴하고 있다. 이상하다.

  • 위 코드는 문장이기 때문에 값이 들어가야할곳(매개변수,변수의 값할당)에 들어갈수없다.

함수 선언, 함수 표현식, 기명 함수 표현식

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

  • 함수의 이름을 출력해주는 함수를 선언하였다.

  • 이것은 statement이기 때문에 값으로 귀결되지 않는다. expression이 아니라 statement이다.

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

  • 이번에는 console.log() 함수의 인자로 foo 함수를 넘겼다.

  • 여기서 사용된 foo 함수는 표현식으로써 사용되었기 때문에 값이 들어가야할곳에 들어갈 수 있었다.

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

  • 이번엔 foo함수로 넘기는 함수의 이름을 붙인채로 넘겼다.

  • 이것이 기명 함수 표현식이다.

  • 마찬가지로 foo라는 함수의 호출이기 때문에 해당문장은 표현식이고 값으로 귀결되어 console.log의 인자로 전달이 가능해서 오류가 나지 않았다.

자바스크립트는 값으로 해석되어야 하는 부분에 함수가 선언되어 있으면 그것을 표현식으로 해석하려고 한다. 하지만, 그렇지 않은 나머지 모든 부분에 함수가 선언되어 있으면 그것은 함수 선언으로서 해석한다.

예제 - 과연 다음 함수선언 들은

함수 문장일까요 함수 표현식일까요?

if () { function foo () {} // 함수 문장, 값이 필요하지 않은 곳에 함수선언 } function foo () {} // 함수 문장, 값이 필요하지 않은 곳 function foo () { function bar () {} // 함수 문장, 값이 필요하지 않은곳 } function foo () { return function bar () {} // 함수 표현식 - 값이 필요한곳, 기명 함수 표현식 } foo(function () {}) // 함수 표현식 - 값이 필요한곳, 익명 함수 표현식 function foo () { return function bar () { function baz () {} //bar는 함수 표현식이므로 함수이름을 생략해도 됨, baz는 생략불가(문장) } } function () {} // syntax error : 함수 문장은 무조건 함수의 이름이 주어져야 한다.

중요! 함수 문장 vs 함수 표현식 함수 문장으로 함수를 선언할때는 무조건 함수의 이름이 있어야 한다. 하지만 함수 표현식으로 함수를 선언할때는 함수의 이름이 있어도되고 없어도 된다.

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

표현식(Expression)은 문장(Statement)처럼 동작할 수 있다. → 표현문(Expression Statement) → 값으로써 귀결도 되면서, 다른 변수들의 상태도 바꾸는 함수가 표현문의 예인듯?? → 하지만 반대로, 문장은 표현식이 될 수 없다.

2+2; //2+2자체는 표현식이나 마지막에 세미콜론을 붙여서 표현문으로 만들어줬음 foo(); //foo() 자체는 값으로 귀결되는 표현식, 마지막에 ;때문에 문장이 되어버림. foo(2+2;) // 문법 에러(Syntax Error), 표현식이 와야할곳에 문장이 왔으므로

여러줄의 문장을 하나의 줄로 만들기

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

  • 3개의 문장이 한줄로 표시되었다.

그럼 여러줄의 표현식을 하나로 묶고 싶을땐? → 콤마(,) 사용

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의 인자로 넘기게 되므로 주의하자.

console.log( 3, true ? 2+2 : 1+1 ) // 3, 4 출력됨 -> console.log()함수가 둘다 출력해버림. console.log( (3, true ? 2+2 : 1+1) ) // 자바스크립트 엔진에게 표현식 묶음이 전달됨, 4출력

여기부터 아주 중요!!!

아까 위에서 살펴본것중에 매우 중요해서 다시 한번 짚고 넘어가야 할 것들이 있다.

  • 표현식은 값으로 귀결된다.

  • 값이 필요한곳에 함수가 선언되어있으면 그 함수는 표현식으로 해석된다.

    function() {} //함수 문장은 무조건 이름이 있어야 한다.

  • 위 함수는 값이 필요하지 않은 위치에 선언된 함수이므로 함수 문장이다.

  • 함수 문장은 익명함수일수없다. 무조건 이름이 있는 기명함수여야 한다.

  • 따라서 위 문장은 에러가 난다.

    (function () {}) // function(){}

  • 반면, 위 코드는 문제가 없다. 우선 표현식은 괄호와 콤마로 하나로 묶을수 있다고 했다.

  • 자바스크립트 엔진은 위 코드를 표현식의 묶음(비록 표현식이 하나지만)으로 해석을 하기 때문에 위 함수 선언을 표현식으로 해석한다.

  • 그리고 마지막 표현식을 리턴하는데 여기서는 표현식이 하나기 떄문에 바로 저 괄호 안에 있는 함수 자체를 리턴하게 된다.

  • 만약 다음과 같이 호출하더라도 같은 표현식이 리턴된다.

    (4, 5, function() {}) // function(){}

이제 다음 코드가 왜 익명함수 호출로써 가능했었던건지 드디어 이해할수있다.

(function (){ //code.. })()

  • 많은 자바스크립트 책에서 익명함수를 즉시 호출할때는 위와같이 함수를 괄호로 감싼다음에 호출문()를 붙이라고 한다. 하지만 그 이유에 대해선 설명해주는 책이 없었다.
  • 이제 이 코드를 해석할 수 있게 되었다. 자세히 살펴보자.

위 코드를 한줄로 펴보면 다음과 같다.

(function (){}) ()

  • 앞부분만 따로 봤을때, 표현식을 괄호로 묶은것이고 괄호 안에 표현식이 하나이며 그 자체가 리턴되어 자바스크립트 엔진에 의해 값으로 해석되어, function(){} 익명함수 자체가 리턴된다.
  • 그리고 그 값으로 귀결된 익명함수를 함수 호출문 ()로 바로 호출하고 있다.

그럼 다음과 같은 코드도 가능하다.

var func = (function (){}); //표현식 묶음의 결과 익명함수가 표현식으로 리턴된다. var func = function(){}; //이렇게 익명 함수 표현식이 가능한 이유도, 값이 필요한 자리에 함수 선언이 왔기 때문이다. 즉 이 익명함수는 값으로 써 취급되고 있다. console.log(func) // f(){}

이제 다음과 같은 문장들을 해석할수 있게 되었다.

(function () { console.log("익명함수 즉시 호출"); })() // "익명함수 즉시 호출" (function () { return 3; })() // 3 console.log((function () { return 3; })()) // 3 // 인자를 넘길 수도 있습니다. (function (a) { return a; })("저는 인자입니다."); // 저는 인자입니다.

표현식을 묶을땐 (), 문장을 묶을땐 {}

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

  • 문장들을 {} 블록으로 묶었다. {}블록 내부의 문장들은 그룹화되지만 각각 모두 실행된다. → console.log(a);를 출력해도 'b'가 찍힌다.

     

  • 이것은 객체 리터럴과는 다른것이다. 객체리터럴은 key와 value를 :로 구분하고 {}블록 안에 콤마로 멤버들을 구분한다.

  • 표현식 그룹화 () 때와 마찬가지로 문장 그룹화 {}블록에서도 마지막 문장이 리턴된다.

  • cf) 2+2; 는 표현식이 아니라 표현식 문장이다. 즉, 문장이다.

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

    console.log({var a = "b"; func(); 2+2}) // 문법 에러, {}의 결과 문장이 리턴되서

    console.log((var a = "b", func(), 2+2)) //에러 var a ="b"가 문장이기 때문에.

    const obj = {var a = "b", func(), 2+2} // 문법에러, 문장은 값으로 취급될 수 없다.

  • 객체 리터럴은 표현식이다. 그래서 console.log()의 인자로 넘길 수 있다.

오늘의 하이라이트 - 이 코드를 이해하기 위해 여기까지 왔다.

{} + 1 // 1 {2} + 2 // 2 {2+2} + 3 // 3 {2+2} - 3 // -3

  • 값이 와야 할곳에 블럭그룹화{}이 오고 있다.
  • {}블럭내의 마지막 문장이 리턴되지만, 문장은 값으로 귀결되지 않는다.
  • 자바스크립트는 문장이 값이 되어야 할때 바로 에러를 내지는 않는다. 숫자나 문자열로 변환시켜보고 그것도 불가능하면 에러를 낸다.
  • 따라서, 리턴된 문장을 자바스크립트가 그냥 0으로 강제 형변환하고 피연산자로 사용된다.

반면, 다음 코드는 정확히 예상했던대로 동작한다.

{} + 1 // 1 {2} + 2 // 2 {2+2} + 3 // 3 {2+2} - 3 // -3

댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함