deep dive 12장 함수

@hanminss·2021년 11월 30일
0

Deep Dive

목록 보기
6/16
post-thumbnail

12장 함수

1. 함수란?

  • 함수는 일련의 과정을 문으로 구현하고 코드 불록으로 감싸서 하나의 실행 단위로 정의한 것
function add(a,b){ // add: 함수이름 a,b: 매개변수
  return x+y; // x+y: 반환값
}
add(2,3);// 2,5: 인수

2. 함수를 사용하는 이유

  • 함수는 몇 번이든 호출할 수 있으므로 코드의 재사용이라는 측면에서 매우 유용하다.
  • 유지보수의 편의성을 높이고 코드의 신뢰성을 높인다.
  • 코드의 중복을 억제하고 코드의 가독성을 향상시킨다.

3. 함수 리터럴

  • 리터럴은 값을 생성하기 위한 표기법이었다.
  • 함수 리터럴도 평가되어 값을 생성하며 이 값은 객체이다. 즉 함수는 객체이다.
  • 함수와 일반객체의 차이점은 함수는 호출을 할 수 있다는 점이다.
  • 또한 일반객체에는 없는 함수 객체만의 고유한 프로퍼티를 갖는다.
  • 함수가 객체라는 점은 다른 프로그래밍 언어와 구별되는 js의 중요한 특징이다.
  • 함수 이름은 함수 몸체에서만 참조 가능하다. (우리가 평소에 그냥 사용 가능한건 js엔진이 식별자를 암묵적으로 만들기 때문이다)

4. 함수 정의

1. 함수 선언문

- 함수 선언문은 함수 리터럴과 형태가 동일하다.
- 함수 선언문은 이름을 생략할 수 없다.
- 함수 선언문은 표현식이 아닌 문이다.
- 본래 변수에 표현식이 아닌 문은 할당할 수 없다.
- js 엔진은 {}를 사용하면 코드블록으로 해석할지 객체 리터럴로 해석할지 정하게 된다.
- 이는 문맥의 따라 해석이 달라진다.
- 따라서 js 엔진은 이름이 있는 함수 리터럴만이 단독으로 사용되었다면 함수 선언문으로 해석한다.
- 변수에 함수 리터럴이 정의 되어 있거나 피연산자로 사용하면 함수 리터럴 표현식으로 해석하게 된다.
- js 엔진은 함수 선언문을 함수 표현식으로 변환해 함수 객체를 생성한다고 볼 수 있다.
// 함수 선언문
function add(x,y){
  return x+y;
}
// js 엔진은 {}를 만나면 함수 선언문으로 해석할지 함수 리터럴 표현식으로 해석할지 정하게 된다.

// 함수 리터럴만이 단독으로 선언되었으므로 함수 선언문으로 해석된다.
function foo() {
  console.log("foo");
}

// 변수에 함수를 할당하여 함수 표현식이 되었다.
var foo2 = function foo2() {
  console.log("foo2");
};

foo();
foo2();
// 두 가지 방법은 과정은 다르지만 결과는 같다.

// 함수를 그룹연산자 () 내에 함수 리터럴을 넣어 피연산자가 됐다. 이는 함수 리터럴 표현식으로 해석된다.
(function bar() {
  console.log("bar");
});

bar(); // reference err
// 함수의 식별자는 원래 함수의 몸체에서만 참조할 수 있다.
// 따라서 위의 bar는 함수의 외부에서 참조할 수 없기 떄문에 호출할 수없다.
// 하지만 위의 foo나 foo2는 호출이 가능하다, 이는 js 엔진이 암묵적으로 함수 객체를 가리키는 식별자를 생성한다.
// js 엔진은 함수 선언문은 암묵적으로 함수 객체를 가리키는 식별자를 따로 생성한다.
// 이것이 함수 선언문이 꼭 기명함수를 만들어야 하는 이유인 것 같다.
// 따라서 위의 함수 리터럴 표현식을 참조하려면 식별자를 따로 붙혀줘야 한다.
bar = (function bar(){
  console.log("bar");
});
bar(); // O
// 결과적으로 js 엔진은 함수 선언문을 함수 표현식으로 변환해 함수 객체를 생성한다고 볼 수 있다.

2. 함수 표현식

- 자바스크립크 함수는 값처럼 변수에 할당, 프로퍼티의 구성, 배열의 요소등이 될 수 있다.
- 값의 성질을 갖는 객체를 일급객체라 하며 js 함수는 일급객체이다.
- 함수는 일급객체이므로 변수에 할당할 수 있다. 이러한 방식을 **함수 표현식** 이라 한다.
- 함수 표현식에서의 리터럴에서 이름은 생략할 수있다. 이러한 함수를 익명함수라 한다.
- 함수 표현식에서는 이름을 생략하는게 일반적 방법이다.
- 위에서 언급했듯 함수 표현식은 암묵적인 식별자를 만들어주지 않기에 익명함수로 쓸 수 있다.
- 함수 선언문과 함수 표현식의 차이는 함수 선언문은 표현식이 아닌 문이고 함수 표현식은 표현식인 문이다. 이는 미묘하지만 큰 차이가 있다.
var add = function add(x,y){
  return x+y;
}

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

- 함수 선언문으로 생성한 함수는 선언문이 정의되기 이전에 호출될 수 있다.
- 함수 표현식으로 생성한 함수는 선언문이 정의되기 이전에 호출될 수 없다.
- 함수 선언문으로 함수를 정의하면 런타임 이전에 함수 객체가 먼저 생성된다. 또한 암묵적으로 식별자까지 만들어서 런타임 이전에 할당까지 완료된다.(함수 호이스팅)
- 함수 호이스팅과 변수 호이스팅의 차이는 변수 호이스팅은 undefined로 초기화하고 함수 호이스팅은 함수 객체로 초기화 된다는 것이다.
- 따라서 변수는 선언 전 호출하면 undefined로 평가되지만 함수는 선언문 이전에 호출하면 함수 호이스팅에 의해 호출이 가능하다.
- 반면에 함수 표현식은 변수 호이스팅이 되기 때문에 undefined로 초기화 된다. 함수 표현식이 실행되는 시점에 함수객체가 된다.
- 함수를 호출하기전에 함수를 선언해야한다는 프로그래밍의 중요한 규칙을 무시한다는 문제 때문에 JSON의 창시자는 함수 표현식 사용을 권장한다고 한다.

4. function 생성자 함수

- 생성자 함수로 함수를 생성하는 방식은 일반적이지 않으며 바람직하지도 않다.
- 생성자 함수로 만든 함수는 클로저를 생성하지 않는 등 다른 방법을 정의한 함수와 다르게 동작한다.
var add = new function('x', 'y', 'return x+y');

5. 화살표 함수(ES6)

- 간략한 방법으로 함수를 선언할 수 있다.(es6)
- 화살표 함수는 항 상 익명 함수로 정의한다.
- 표현만 간략한것이 아닌 내부 동작 또한 간략화 된다.
	- 1. this 바인딩 방식이 다르다.
	- 2. prototype 프로퍼티가 없다.
    - 3. arguments 객체를 생성하지 않는다.
    - 4. 생성자 함수로 사용할 수 없다.
var add = (x,y) => x+y;

5. 함수 호출

1. 매개변수와 인수

- 함수는 매개변수의 개수와 인수의 개수가 일치하는지 확인하지 않는다.
- 인수의 개수를 맞추지 않아도 에러가 나지 않고 인수가 부족하면 undefined가 된다.
- 인수가 더 많다면 초가된 인수는 무시된다.
- 모든 인수는 함수의 arguments 객체로 보내진다.

- 함수 자체적으로 확인을 해주지 않기 때문에 단축평가나 if문 등으로 유효성 검사를 만들어줘야한다.
- 아래와 같은 방법으로 default 값을 설정해 줄수도 있다.

function add(a=0, b=0, c=0){
  return a+b+c;
}
- 이상적인 함수는 한가지 일만 하는게 좋으면 가급적 작게 만들어야 한다.
- 매개변수를 3개 이상으로 쓰는 것을 권장하지 않는다.

2. 반환문

- 반환문의 역활 1: 함수의 실행을 중단하고 함수 몸체를 빠져나간다.
- 반환문의 역활 2: return 뒤에 오는 표현식을 평가해 반환한다.
- 명시적으로 표현식을 지정하지 않으면 undefined가 반환된다. return을 생략해도 같은 결과
- 반환문은 함수 내부에서만 사용할 수 있다.

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

  • 함수 내의 배개변수도 값에 따라 값에의한 전달, 참조에 의한 전달 방식을 그대로 따른다.
  • 함수가 외부상태를 변경하면 상태 변화를 추적하기 어려워진다.
  • 이를 위해 옵저버 패턴등을 통해 추적하기 쉽게 한다.
  • 다른 방법은 객체를 불변 객체로 만들어 사용하는 방법이 있다.
  • 외부 상태를 변경하지 않고 외부 상태에 의존하지도 않는 함수를 순수함수라 한다.
function changeVal(primitive, obj) {
  primitive += 100;
  obj.name = "kim";
}

var num = 100;
var person = { name: "lee" };

console.log(num);
console.log(person);

changeVal(num, person);

console.log(num); // 값에 의한 전달이라 훼손되지 않는다.
console.log(person); // 참조에 의한 전달이라 이름이 바뀐다.

7. 다양한 함수의 형태

1. 즉시 실행함수

- 함수 정의와 동시에 실행된다.
- 단 한번만 호출되며 다시 호출될 수 없다.
- 기명 즉시 실행함수는 함수 리터럴로 평가되며 다시 호출할 수 없다.(함수 표현식으로 만들면 가능)
- 즉시 실행 함수는 그룹연산자로 감싸야 한다.
- 
(function (){
var a=3;
var b=5;
return a*b;}());

2. 재귀함수

- 재귀 함수는 자기 자신을 호출 하는 행위, 즉 재귀 호출을 수행하는 함수를 말한다.
- 반복되는 처리를 위해 사용된다.
- 반복문 없이 반복처리를 할 수 있다는 장점이 있다.( 이게왜 장점이지?)
- 무한 반복에 빠질 위험이 있고 스택 오버플로우를 발생시킬 수있다.
function factorial(n){
	if(n<=1) return 1;
  	return n*factorial(n-1);
}

3. 중첩함수

- 함수 내부에 정의된 함수
- 일반적으로 자신을 포함하는 외부 함수를 돕는 헬퍼 함수의 역할을 한다.

4. 콜백함수

- 함수의 매개변수를 통해 다은 함수의 내부로 전달되는 함수를 콜백 함수라 한다.
- 매개 변수를 통해 함수의 외부에서ㅏ 콜백함수를 전달받은 함수를 고차함수라 한다.
- 콜백 함수도 고차 함수에 전달되어 헬퍼 함수의 역할을 한다.
- 콜백 함수가 고차함수 내부에만 사용된다면 익명 함수 리터럴로 정의해 바로 넣어버리는게 일반적이다.
- 하지만 전달된 함수 리터럴은 고차 함수가 호출될 때마다 평가되어 함수 객체를 생성한다.
- 따라서 다른곳에서 호출되거나 고차함수가 자주 호출된다면 외부에 함수를 정의하는 것이 효율적이다.
 repeat(5, function(i){
   if(i%2) console.log(i);
 });

5. 순수함수와 비순수함수

- 순수 함수 : 어떤 외부 상태에 의존하지도 않고 변경하지 도 않는 함수
- 비순수 함수 : 외부 상태에 의존하거나 외부 상태를 변경하는 함수
- 순수함수를 통해 부수효과를 최대한 억제해 오류를 피하고 프로그램의 안정성을 높혀야 한다.

0개의 댓글