study: javascript | 숨참고 deep dive (12) 함수 - 221204

Lumpen·2022년 12월 3일
0

Study

목록 보기
15/92

1. 함수

함수는 자바스크립트에서 가장 중요한 핵심 개념이다
다른 핵심 개념인 스코프, 실행 컨텍스트, 클로저, this 등이 모두 함수와 깊은 관련이 있다

수학의 함수는 input을 받아 output을 출력하는 일련의 과정을 정의한 것이다
프로그래밍의 함수도 같은 개념이다

프로그래밍 언어의 함수는 과정을 문으로 구현하고
코드 블록으로 감싸 하나의 실행 단위로 정의한 것이다
이 때 함수로 전달받는 변수를 매개변수, 실제로 입력되는 값을 인수, 출력을 반환 값이라고 한다
함수는 함수 정의를 통해 생성된다
함수를 실행하기 위해서는 함수 실행문인 함수 호출을 한다

2. 함수를 사용하는 이유

정의한 함수의 실행 시점을 개발자가 정할 수 있고 재사용이 가능하다
코드의 재사용 측면에서 매우 유리
재사용을 한다는 것은 반복을 통한 실수 휴먼 에러를 방지함으로 코드의 신뢰성을 높일 수 있다
유지보수도 편리성, 코드 가독성을 높이는 데에도 도움이 된다

3. 함수 리터럴

자바스크립트의 함수는 객체 타입의 값이다
함수는 함수 리터럴로 생성할 수 있다
함수 리터럴은 function 키워드, 함수 이름, 매개변수 목록, 함수 몸체로 구성된다
함수 이름은 식별자로 네이밍 규칙을 준소해야 한다
함수 이름은 함수 몸체 내에서만 참조할 수 있는 식별자다
함수 이름은 생략할 수 있다 - 이름이 있는 함수를 기명함수, 이름이 없는 함수를 익명함수라고 한다

각 매개변수의 순서는 의미가 있다
매개변수는 함수 몸체에서 변수와 같이 취급된다

리터럴은 사람이 이해할 수 있는 문자 또는 약속된 기호를 사용해 값을 생성하는 표기 방식이다
리터럴은 값을 생성하기 위한 표기법으로 함수 리터럴도 값으로 평가되며
생성되는 값은 객체다
함수는 객체지만 일반 객체와는 다르다
함수는 호출할 수 있는 특별한 객체다 함수만 가지는 고유한 프로퍼티를 갖는다
함수가 객체라는 사실은 다른 언어와 구별되는 자바스크립트의 중요한 특징이다

4. 함수 정의

정의된 함수는 자바스크립트 엔진에 의해 평가되어 함수 객체가 된다
함수를 정의하는 방법은 4가지가 있다

  • 함수 선언문: function 키워드 사용
  • 함수 표현식: 변수에 함수를 담는 스타일
  • 생성자 함수: new Function() 키워드 사용
  • 화살표 함수 (ES6): () => {}
    모든 함수 정의 방식은 함수를 정의한다는 면에서 동일하지만 각각의 차이가 있다

변수는 선언하지만 함수는 정의한다고 표현했다
함수 선언문이 평가되면 식별자가 암묵적으로 생성되고 함수 객체가 할당된다

4-1. 함수 선언문

함수 선언문은 함수 리터럴과 형태가 동일하지만 이름을 생략할 수 없다
함수 선언문은 표현식이 아닌 문이다
표현식이 아닌 문은 변수에 할당할 수 없다
그러나 함수 표현식은 변수에 할당되는 것 처럼 보인다

아래와 같이 할 수 있다

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

자바스크립트 엔진은 코드의 문맥에 따라 함수 리터럴을
표현식이 아닌 문인 선언문으로 해석하는 경우와
표현식인 문인 함수 리터럴 표현식으로 해석하는 경우가 있기 때문이다

함수 선언문은 함수 이름을 생략할 수 없다는 점을 제외하면 리터럴과 형태가 동일하다
함수 이름이 있는 기명 함수 리터럴은 함수 선언문 또는
함수 리터럴 표현식으로 해석될 가능성이 있다는 뜻

기명 함수 리터럴도 중의적인 코드로 문맥에 따라 해석이 달라질 수 있다
자바스크립트 엔진은 기명 함수 리터럴을 단독으로 사용하면(함수 리터럴을 피연산자로 사용하지않는 경우) 함수 선언문으로 해석하고
함수 리터럴이 값으로 평가되어야 하는 문맥 (변수 할당이나 피연산자로 사용할 경우)
에는 함수 리터럴 표현식으로 해석한다
선언문이든 표현식이든 함수가 생성되지만, 내부 동작에는 차이가 있다

function foo() {console.log('foo')}  // foo
(function bar() {console.log('bar')}) // ReferenceError: bar is not defined

함수 리터럴 foo 는 함수 선언문으로 해석된다
하지만 그룹 연산자 () 내에 있는 함수 리터럴 bar는 표현식으로 해석된다
그룹 연산자의 피 연산자는 값으로 평가될 수 있는 표현식이어야 한다

이처럼 기명 함수 리터럴은 함수 선언문 또는 표현식으로 문맥에 맞게 해석된다

함수 리터럴에서의 함수 이름은 함수 몸체 내에서만 참조할 수 있는 식별자다
따라서 bar는 () 외부에서 호출할 수 없다
foo 는 자바스크립트 엔진이 암묵적으로 생성한 식별자이다
자바스크립트 엔진은 함수 선언문을 해석해 함수 객체를 생성한다
함수 이름은 함수 내부에서만 참조 가능한 식별자로
메모리 공간에 함수 이름을 갖는 함수 객체를 생성 후 함수 이름과 같은 이름의
변수를 생성, 함수가 있는 메모리 공간의 주소를 참조하고 있는 것이다

함수는 함수 이름으로 호출하는 것이 아닌 함수 객체를 가리키는 식별자로 호출한다
자바스크립트 엔진은 함수 선언문을 함수 표현식으로 변환해 함수 객체를 생성한다
그치만 함수 선언문과 표현식이 정확하게 동일한 동작을 하진 않는다

4-2 함수 표현식

함수는 객체 타입의 값이다
자바스크립트의 함수는 값처럼 변수에 할당할 수 도 있고
프로퍼티 값이 될 수도 있으며 배열의 요소가 될 수도 있다
이처럼 값의 성질을 갖는 객체를 일급 객체라고 한다
자바스크립트의 함수는 일급 객체다
함수를 값처럼 자유롭게 사용할 수 있다

함수는 일급 객체이므로 생성한 함수를 변수에 할당할 수 있다

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

함수 리터럴의 함수 이름은 생략할 수 있다
함수 표현식은 주로 익명 함수를 사용하는 것이 일반적

함수를 호출하는 것은 함수를 가리키는 변수의 이름으로 호출할 수 있다
함수 이름은 함수 몸체 내부에서만 참조할 수 있는 이름이다

함수 선언문을 사용하면 자바스크립트 엔진이 함수 이름과 같은 이름의 변수를 식별자로 암묵적 생성하여 참조
함수 선언문은 표현식이 아닌 문, 함수 표현식은 표현식인 문이다

4-3. 함수 생성 시점과 함수 호이스팅

함수 선언문으로 정의한 함수는 함수 선언문 이전에 호출할 수 있다
자바스크립트 엔진에 의해 모든 선언이 호이스팅 되기 때문이다
함수 표현식은 함수 표현식 이전에 호출될 수 없다
함수 선언문으로 정의한 함수와 표현식으로 정의한 함수의 생성 시점이 다른 것이다

함수 선언문으로 작성하면 런타임 이전에 이미 호이스팅이 일어나 함수 객체를 생성, 식별자를
암묵적으로 생성하고, 함수 객체를 식별자에 할당한다

함수 호이스팅과 변수 호이스팅은 비슷하지만 다르다
변수 호이스팅은 초기화를 undefined 로 하지만
함수 호이스팅은 초기화를 함수 객체로 한다

함수 표현식은 변수에 할당되는 값이 함수 리터럴인 문이다
따라서 함수 표현식은 변수 선언문과 변수 할당문을 한 번에 기술한 축약 표현과 동일하다
변수 할당문의 값이 할당문이 실행되는 시점인 런타임에 평가되기 때문에
함수 표현식의 함수 리터럴도 할당문이 실행되는 시점에 평가되어 함수 객체가 된다
따라서 함수 표현식으로 함수를 정의하면 함수 호이스팅이 아닌 변수 호이스팅이 일어난다

함수 호이스팅은 함수를 호출하기 전에 반드시 선언해야 한다는 규칙을 무시한다
때문에 함수 표현식을 권장한다

4-4. 생성자 함수

사용하지 않는다
자바스크립트 빌트인 함수인 Function 생성자 함수에 매개변수 목록과
함수 몸체를 문자열로 전달하면서 new 연산자와 함께 호출하면 함수 객체를 생성해서 반환한다
new 연산자를 쓰지 않아도 동일하게 동작한다
생성자 함수는 객체를 생성하는 함수다

const add = new Function('x', 'y', 'return x + y')

생성자 함수로 함수를 생성하는 것은 일반적이지 않으며 바람직하지 않다
생성자 함수는 클로저를 생성하지 않는 등 다른 함수와 다르게 동작한다

4-5. 화살표 함수

ES6 에 도입된 화살표 함수는 좀 더 간략한 방법으로 함수를 선언할 수 있다
항상 익명 함수로 정의한다
화살표 함수는 기존의 선언 방식을 완전히 대체하기 위해 등장한 것은 아니다
표현만큼 내부 동작 또한 간략화되어 있다

화살표 함수는 생성자 함수로 사용할 수 없고, this 바인딩 방식(전역객체에 바인딩)이 다르고
prototype 프로퍼티도 없고, arguments 객체를 생성하지 않는다

리액트 함수 컴포넌트에서는 무조건 화살표 함수를 사용하는 편이 좋을 것 같다
위의 네가지를 사용할 일이 없다

5. 함수 호출

함수는 함수를 가리키는 식별자와 한 쌍의 소괄호인 함수 호출 연산자로 호출한다
함수 호출 시에는 소괄호 내에 0개 이상의 인자를 쉼표로 구분하여 호출한다
함수 호출 시 원래의 실행 흐름을 중단하고 호출한 함수로 실행 흐름을 옮긴다 (제어권..?)
이 때 매개변수에 인수가 순서대로 할당되고 함수 몸체의 문들이 실행되기 시작한다

5-1. 매개변수와 인수

함수를 실행하기 위해 필요한 값을 함수 외부에서 함수 내부로 전달할 필요가 있는 경우, 매개변수를 통해 인수를 전달한다
인수는 값으로 평가될 수 있는 표현식이어야 한다 인수는 함수를 호출할 때 지정하며 개수와 타입에제한이 없다
매개변수는 함수를 정의할 때 선언하며 함수 몸체 내부에서 변수와 동일하게 취급된다
함수가 호출되면함수 몸체 내에서 암묵적으로 배개변수가 생성되고 일반 변수와 마찬가지로
undefined 로 초기화된 이후 인수가 순서대로 할당된다
함수가 호출될 때 마다 매개변수는 이와 같은 단계를 거친다

매개변수는 함수 몸체 내부에서만 참조할 수 있고 외부에서는 참조 불가
매개변수의 스코프는 함수 내부다
함수는 매개변수와 전달된 인수의 개수가 일치하는지 체그하지 않는다
매개변수보다 적은 인수를 전달하는 경우에도 에러가 발생하지 않는다
인수로 전달되지 않은 매개변수는 undefined인 채로 유지된다

인수가 더 많은 경우 초과된 인수는 무시된다
암묵적으로 arguments 객체의 프로퍼티로 보관된다
-> 화살표 함수라면 없음

arguments 객체는 함수를 정의할 때 매개변수 개수를 확정할 수 없는 가변 인자 함수를 구현할 때 유용하게 사용되지만 나머지 연산자 문법을 사용하면 되기 때문에
굳이 사용할 것은 아니라고 보여진다

arguments 객체는 유사 배열로
배열 형태이지만 배열 함수를 사용할 수 없다

5-2. 인수 확인

인수확인은 타입스크립트를 쓰면 해결된다

ES6 에서는 매개변수 기본값이 도입되었는데
전달되는 인수가 없거나 undefined 일 때만 유효하다

5-3. 매개변수의 최대 개수

ECMAScript 에서는 매개변수의 개수에 대해 제한하고 있지 않다
그러나 함수는 한 가지 일만 해야하며, 가급적 작게 만들어야 한다
따라서 3개 이상의 매개변수는 권장하지 않는다
혹은 객체로 전달 받는 편이 유리하다
객체를 사용하면 매개변수의 순서를 신경쓰지 않아도 되기 때문에 실수가 줄어든다
가독성 또한 좋아짐
하지만 전달된 객체를 변경하게 되면 외부의 객체도 함께 변경되기 때문에 주의해야 한다

5-4. 반환문

함수는 return 키워드와 표현식(반환값) 으로 실행 결과를 외부에 반환할 수 있다

함수 호출은 표현식으로 반환 값으로 평가된다

반환문의 역할은 두가지로
1. 함수의 실행을 중단하는 역할
2. return 뒤의 표현식을 평가해 반환한다 작성하지 않으면 undefined

node.js 는 모듈 시스템에 의해 파일별로 독립적 파일 스코프를 갖기 때문에
파일의 가장 바깥 영역에서 return 을 사용해도 에러가 발생하지 않는다

6. 참조에 의한 전달과 외부 상태 변경

원시값은 변경 불가능한 값으로 전달된 값이 어떻게 사용되든 외부에 영향을 미치지 않지만
객체 등이 전달되었을 경우 내부에서 변경된 값이 외부에 영향을 주게 된다

함수가 외부의 상태를 변경하게 되면 상태 변화를 추적하기 어려워지기 때문에
불변성을 지키는 편이 좋다
객체나 리스트를 복사하는 비용이 들지만 그 것이 훨씬 안전하고 읽기 쉬운 코드를 만든다

외부 상태를 변경하지 않고 의존하지도 않는 함수를 순수 함수라고 부른다
부수효과를 최대한 억제하여 오류를 피하고 프로그램의 안정성을 높이는 프로그래밍 패러다임을
함수형 프로그래밍이라고 부른다

7. 다양한 함수의 형태

7-1. 즉시 실행 함수

함수 정의와 동시에 즉시 호출되는 함수를 즉시 실행 함수라고 한다
단 한 번만 호출되며 다시 사용할 수 없다

(() =>console.log('즉시 실행'))()

즉시 실행 함수는 이름이 없는 익명 함수를 사용하는 것이 일반적이지만
이름이 있어도 즉시 실행 함수로 사용할 수 있다
하지만 그룹 연산자 () 내부의 기명 함수는 선언문이 아니라 함수 리터럴로 평가되며
함수 이름을 재사용 할 순 없다

그룹 연산자의 피연산자는 값으로 평가되므로 기명 또는 무명 함수를 그룹 연산자로 감싸면
함수 리터럴로 평가되어 함수 객체가 된다

그룹 연산자로 함수를 묶는 이유는 함수 리터럴을 평가해서 함수 객체를 생성하기 위함이다
즉시 실행 함수도 값을 반환할 수 있고 인수를 전달할 수도 있다

즉시 실행 함수를 사용하면 혹시 모를 변수나 함수 이름의 충돌을 방지할 수 있다

7-2. 재귀 함수

함수가 자기 자신을 호출하는 것을 재귀 호출이라고 하고
재귀 호출을 하는 함수는 재귀 함수라고 부른다
재귀 함수를 사용하면 반복문 없이 반복 작업을 구현할 수 있다

함수 몸체 내부에서는 함수 이름을 참조할 수 있기 때문에
함수 이름을 통해 자기 신을 호출할 수 있다

재귀 함수는 기본적으로 무한히 실행되기 때문에
탈출 조건을 마련해야 한다
탈출 조건이 없으면 스택 오버플로우 발생

대부분의 재귀 함수는 반복문으로 구현 가능하다

재귀 함수는 반복문 보다 이해하기 쉬운 코드를 작성하기 위해서만 사용하는 편이 바람직하다

7-3. 중첩 함수

함수 내부의 함수를 중첩 함수 또는 내부 함수라고 한다
밖에 있으면 외부 함수

중첩 함수는 외부함수 내에서만 호출할 수 있다
주로 내부 함수의 역할은 외부 함수를 돕는 헬퍼 함수로써 기능을 한다

ES6 부터 함수 정의는 문이 위치할 수 있는 모든 곳에서 사용 가능하다
if 문이나 for 문 내부에서도 정의할 수 있지만
그냥 안쓰는 편이 좋다
호이스팅으로 인해 혼란이 발생할 수 있다

중첩 함수는 스코프와 클로저에 관련이 있다

7-4. 콜백 함수

함수의 매개변수를 통해 다른 함수의 내부로 전달되는 함수를 콜백 함수라고 하며
매개변수를 통해 함수의 외부에서 콜백함수를 전달받은 함수는 고차함수라고 한다

매개변수를 통해 함수를 전달 받거나 반환값으로 함수를 반환하는 것을
함수형 프로그래밍에서 고차함수라고 한다
콜백 함수도 고차 함수로 전달되어 헬퍼 함수의 역할을 한다
중첩 함수는 함수 내부에 정의되어 있어 교체할 수 없지만
콜백 함수는 함수 외부에서 함수 내부로 주입하기 때문에 자유롭게 교체할 수 있다
고차 함수는 콜백 함수는 자기의 일부분으로 합성한다

고차 함수는 매개ㅔ변수를 통해 전달받은 콜백 함수의 호출 시점을 결정헤서 호출한다
콜백 함수는 고차 함수에 의해 호출되며
고차 함수는 필요에 따라 콜백 함수에 인수를 전달할 수 있다

콜백 함수로 전달된 함수 리터럴은 고차 함수가 호출될 때마다 평가되어 함수 객체를 생성한다
콜백 함수를 다른 곳에서도 호출할 필요가 있거나 콜백 함수를 전달받는 함수가 자주 호출된다면
함수 외부에서 콜백 함수를 정의한 후 함수 참조를 고차 함수에 전달하는 편이 효율적이다

콜백 함수의 참조를 고차 함수에 전달하면 정의된 콜백 함수는 한 번만 생성된다
하지만 콜백 함수의 정의를 고차 함수에서 익명 함수 리터럴로 정의하면서 전달하면
고차 함수가 호출될 때마다 콜백 함수가 생성된다

콜백 함수는 함수형 프로그래밍 뿐만 아니라 비동기에 활용되는 중요한 패턴이다
배열 고차 함수에도 사용된다

7-5. 순수 함수와 비순수 함수

함수형 프로그래밍에서는 어떤 외부 상태에 의존 하거나 외부 상태를 변경하지 않는
부수 효과가 없는 함수를 순수 함수라고 한다
순수 함수는 동일한 인수가 전달되면 언제나 동일한 값을 반환하는 함수다

함수형 프로그래밍은 순수 함수와 보조 함수의 조합을 통해 사이드 이펙트를 최소화,
불변성을 지향한다
로직 내의 조건문과 반복문을 제거해서 복잡성을 해결하며
변수 사용을 억제하거나 생명주기를 최소화하여 오류를 최소화 하는 것을 목표로 한다

자바스크립트는 멀티 패러다임 언어로 객체지향과 함수형 모두 적극 활용한다

profile
떠돌이 생활을 하는. 실업자는 아니지만, 부랑 생활을 하는

0개의 댓글