본문 바로가기

스터디/모던 자바스크립트 딥 다이브

[딥 다이브 스터디 12장] 함수

<모던 자바스크립트 딥 다이브> (이웅모, 위키북스) 를 읽고 공부한 내용입니다. 책의 내용을 그대로 적어 놓은 것이 아니기 때문에 오류가 있을 수 있습니다. 오류가 있다면 댓글을 통해 피드백 부탁드립니다.

❓ 급 퀴즈

1. 콘솔에는 무엇이 찍힐까요?

var add1 = function add(x, y){
    return x + y;
}
console.log(add1.name) // ???​

2. 다음 함수는 어떤 문제를 가지고 있을까요?

function introduce(name, age, location, hobby) {
  return `Hello! I'm ${name}, ${age} years old, from ${location}, like ${hobby}..`;
}

console.log(introduce(...))

 

[1] 함수란?

어떤 일련의 과정을 '문'으로 구현하고 코드 블록으로 감싸서 하나의 실행 단위로 정의한 것. '인수'를 '매개 변수'로 받아서 출력한다. '함수 이름'을 가질 수 있다.

[1-1] 함수 정의, 함수 호출

함수를 정의하였다면, 호출해야 실행된다. 이 때 매개 변수를 필요로 하는 함수라면 인자를 넘겨줘야 한다.

// 함수 정의
// x, y를 '매개 변수'라 한다.
function add(x, y) {
  return x + y;
}

// 함수 호출
// 1, 2를 '인자'라 한다.
add(1, 2);

[2] 왜 함수를 사용하는가?

1. 코드의 중복을 억제하고 재사용성을 높인다. → 유지보수 편의성 향상, 코드의 신뢰성 향상

2. 적절한 함수 네이밍은 코드 가독성을 향상시킨다.

[3] 함수 리터럴

이전에 메서드를 정의하는 가장 기본적인 구문을 지칭하는 용어는 함수 리터럴 방식이라 한다.

const person = {
    name: 'Sang Yoon',

    // 함수 리터럴로 메서드 정의
    getName : function(){
        return this.name;
    }
}

console.log(person.getName()) // 'Sang Yoon'

즉 변수에 함수를 리터럴 방식으로 할당하는 것이 함수 리터럴 방식이다. 변수에 함수를 할당한다는 것은 흡사 화살표 함수와 비슷해보인다. 함수 리터럴은 값으로 평가된다.

const add = function add(x, y){
    return x + y;
}

const add2 = (x, y) => x + y;

console.log(add(1, 2)) // 3
console.log(add2(3, 4)) // 7

[3-1] 함수는 객체다

  1. 함수 리터럴은 값으로 평가된다.
    • 함수 리터럴을 변수에 할당한다. ( = 함수를 변수에 할당한다) → 변수에 할당할 수 있는 것은 '값'이다.
  2. 함수는 객체다.
    • 아래 add 라는 함수를 선언하고 a라는 key에 2를 추가했더니 추가된 프로퍼티를 확인할 수 있다.
    • add2는 add3을 참조하고 있다. 만약 add2가 무기명(이름이 없는) 함수였다면 add2가 참조하는 무기명 함수는 그대로 add2라는 이름을 사용했을 것이다.
    •  함수 리터럴로 함수를 변수에 할당하면 변수는 함수를 참조한다. (함수는 객체이므로)
  3. 함수는 객체 타입의 '값'이므로 함수 표현식이나 화살표 함수로 변환하여 사용할 수 있다.
  4. 함수가 '값'이라는 것은 자바스크립트에서 함수는 일급이라는 것이다.

[4] 함수 정의

함수를 4가지 방법으로 정의할 수 있다.

  1. 함수 선언문
  2. 함수 표현식
  3. Function 생성자 함수
  4. 화살표 함수

[4-1] '문'과 '식' (feat. 크롬 개발자 도구의 undefined)

크롬 개발자 도구를 사용할 때, 어떤 경우에는 undefined를 출력한다. (💡 새로 알게된 점) 이 이유를 정확히 알지 못했는데, '문'과 '식'의 차이였다. 크롬 개발자 도구에서 '문'을 입력하면 undefined를 출력하고 '식'을 입력하면 값을 출력한다. 즉 값으로 평가된다면 값을, 값으로 평가되지 않는다면 undefined를 출력하는 것이다. 함수 표현식도 undefined를 출력하는데, 함수 표현식은 '표현식'이면서 '문'이기 때문이다.

 

변수에 할당할 수 있는 것은 '값' 이다. 하지만 함수 리터럴에서 변수에 함수 선언문으로 정의된 함수를 할당하는 것 처럼 보인다. 함수 선언문은 '문'이라서 '값'으로 표현될 수 없는데?

[4-2 ~ 4-3] 함수 표현식에서 자바스크립트의 동작 (p160 ~ 163) + 함수 호이스팅

이 부분이 (💡 새로 알게된 점) 이었다. 함수 선언문은 사실, 자바스크립트 엔진이 암묵적으로 함수 이름과 같은 식별자를 만들어 놓고 그것을 실행하는 것이다. 따라서 함수 표현식(함수 리터럴)이 가능한 것이다

함수 선언문은 함수 호이스팅, 함수 표현식은 변수 호이스팅이 발생한다.

자바스크립트 엔진이 현재 실행컨텍스트의 모든 식별자를 평가 할 때 함수도 평가된다. 이것이 함수 호이스팅.

함수 표현식은 함수를 변수에 할당한다. 따라서 이 때 호이스팅은 변수 호이스팅으로 작용.

함수 선언문으로 함수가 정의되면 실행 컨텍스트의 환경레코드에 함수가 기록된다. 하지만 함수 표현식으로 정의된 함수는 변수에 할당된 함수이므로 변수가 기록된다. 즉 var로 선언된 변수(함수가 할당된)라면 undefined로 먼저 평가된다. let이나 const라면 TDZ 상태이므로 참조에러가 출력될 것. 호이스팅 시점에서 var로 선언된 함수(변수)는 undefined로 먼저 평가된 상태이다. undefined는 함수가 아니므로 호출할 수가 없다. 따라서 타입에러가 출력될 것.

이제 런타임 상황에서 자바스크립트 엔진이 var 또는 let 또는 const ??? = function... 을 만나는 시점에 실행컨텍스트의 환경레코드에 비어있던 변수 식별자가 함수로 평가된다. 그리고 함수 객체가 된다.

이러한 자바스크립트의 동작은 함수를 호출하기 전에 당연히 함수를 선언해야 한다는 프로그래밍 흐름을 무시한다. 따라서 더글라스 크락포(<JavaScript: The Good Parts>의 저자이자 JSON을 창안)는 함수 선언문 대신 함수 표현식을 사용할 것을 권장한다.

함수 표현식 중에서 화살표 함수는 렉시컬 컨텍스트를 따르기도 한다. 화살표 함수를 사용하는 것이 여러모로 좋아 보인다. 

[4-4 ~ 4-5] Function 생성자 함수와 화살표 함수

  1. Function 생성자 함수는 별로다. 사용하지 않는 것이 좋다.
  2. 화살표 함수는 무조건적으로 함수 선언문을 대체할 수 없다.
    1. 생성자 함수로 사용될 수 없음
    2. this를 문맥적으로 바인딩(무조건적으로 좋다고 할 수 없는 이유가 this를 문맥적으로 바인딩 하지 않는 고급 테크닉이 있다고 함. 그런데 그게 뭔지는 모르겠음)
    3. prototype 프로퍼티가 없다.
    4. arguments 객체를 생성하지 않는다.

[5] 함수 호출

자바스크립트에서 함수의 매개변수는 제한이 없다. → 사용하는 쪽에서 사용하기가 힘들다.

사용하는 쪽에서 사용하기가 힘들다면, 예상치 못한 버그로 이어질 수 있다.

  1. 매개 변수가 덜 들어오거나, 더 들어와도 에러가 아니다. 이 때 이런 변수를 가변인자라고 한다.
  2. 사용하는 쪽에서 이 함수를 사용하려 할 때, 함수의 스펙을 확인해야 한다. 왜냐하면 매개변수가 몇 개가 들어가는지, 순서는 어떻게 되는지를 확인해야 하기 때문이다. 매개변수 순서를 바꾸기만 해도 함수는 다른 동작을 하게 될 것이다. 유지보수 측면에서도 좋지 않다.
  3. 그래서 매개변수는 3개를 넘지 않는 것이 좋다. 그 이상의 변수가 필요하다면 객체 형태로 변수를 넘겨주는 것이 좋다.
  4. 그래서 코드의 표현을 풍부하게 하기위해 전개연산자나 arguments를 이용해서 가변인자를 처리할 수 있다.

자바스크립트 함수에는 타입 정의가 없다.

가장 기본적인 데이터 타입(원시타입, 객체타입)을 제외하고, 함수가 어떤 값을 반환하는지, 매개변수의 타입은 무엇인지에 대해서 함께 정의하는 것은 매우 중요하다.

급 TMI) 비록 내가 C언어를 제일 처음에 배우기는 했지만 정말정말 기본적인 수준이었고, 그 상태에서 자바를 봤을 때와 자바스크립트를 봤을 때 상당히 난이도 측면에서 갭이 있었다. 자바는 규칙이 엄해서(?) 타입을 빡세게(?) 정의를 해줘야 했는데 자바스크립트는 그렇지 않았기 때문이다. 자바스크립트는 매우 편리했다. 하지만 지금 프로그래밍을 하면 할수록 타입이 없다는 것은 매우 불편한 일이다... (타입스크립트 최고)

자바스크립트에 타입이 없다는 것은 함수가 정확한 동작을 하기 위해서 타입체킹을 매번 해줘야 한다는 것이다. 

[6] 참조와 함수형 프로그래밍

모든 객체가 그러하듯, 함수 내부에 정의된 객체도 참조될 수 있다. 따라서 객체 내부의 값을 불변하게 하기 위해 나온 프로그래밍 패러다임이 함수형 프로그래밍.

[7] 다양한 함수의 형태

  1. 즉시 실행 함수 (단 한번만 실행된다.)
  2. 재귀 함수
  3. 중첩 함수(헬퍼 함수) (❓ 중첩함수는 좋지 않은 코드패턴인지?)
  4. 콜백 함수
    • 함수 자체를 값으로 넘겨준다는 것 → 처음 알았을 때 신세계 (아니 이게 되네....)
  5. 순수함수와 비순수함수 (feat. 함수형 프로그래밍)
    • 순수함수 : 외부 상태를 변경하지 않는 함수
    • 비순수함수 : 외부 상태를 변경하는 함수
    • 따라서 외부 상태를 변경하지 않는 불변성을 지향, 변수의 사용을 억제, 반복문과 조건문을 억제하여 가독성을 향상시키고 프로그래밍의 버그를 줄이고자 하는 프로그래밍 패러다임 → 함수형 프로그래밍

🔗 참고 자료