JavaScript - 함수의 형태가 이렇게 다양하다니,,!

김민재·2021년 8월 1일
0

TIL, Deep Javascript

목록 보기
11/22
post-thumbnail

*🔐Study Keyword :

🔑다양한 함수의 형태에 대해서 파헤쳐보자!

1. 즉시 실행함수

-WHAT IS❓

즉시 실행 함수함수 정의와 동시에 즉시 호출되는 함수 의미한다.

  • 즉시 실행 함수는 단 한 번만 호출되며 다시 호출할 수 없다.

-HOW TO USE❕❓

  • 즉시 실행함수는 익명 혹은 기명 함수 함수 끝에 함수 호출 연산자, ()를 붙인 채 함수 전체를 그륩 연산자 (...)로 감싸준다.
  • 즉시 실행함수는 익명함수를 사용하는 것이 일반적이다.
<script>
// 익명 즉시 실행 함수
(function (){
  let x = 3;
  let y = 5
  return x+y;
}());
</script>
  • 그륩 연산자(...) 내의 기명함수는 함수 선언문이 아닌 함수 리터럴로 평가되기 때문에 함수 이름을 통해 즉시 실행 함수를 다시 호출할 수 없다.
<script>
// 기명 즉시 실행 함수
(function multiply(){
  let x = 3;
  let y = 5
  return x+y;
}());
multiply(); // 참조오류
</script>
  • 그륩 연산자의 피연산자는 값으로 평가되어 기명 또는 무명함수를 그륩 연산자로 감싸면 함수리터럴로 평가되어 함수 객체가 된다.
<script>
// 위 함수 내용 일치
// 익명 즉시 실행 함수 데이터 타입
console.log(typeof (function (){})); // function
// 기명 즉시 실행 함수 데이터 타입
console.log(typeof (function multiply(){})); // function
// 즉시 실행 함수 다른 연산자 사용 방식 
</script>
  • 즉시 실행 함수도 일반 함수처럼 값을 반환할 수 도 있으며 인수를 전달할 수 도 있다.
<script>
// 즉시 실행 함수도 일반 함수 처럼 값을 반환
const multiply = (function(){
  let x = 3;
  let y = 5;
  return x * y;
}());
console.log(multiply) // 15
// 즉시 실행 함수도 일반 함수처럼 인수를 전달
multiply2 = (function(x, y){
  return x * y;
}(3, 5));
console.log(multiply2) // 15
</script>

2. 재귀 함수

-WHAT IS❓

재귀 함수자기 자신을 호출하는 행위, 재귀 호출을 수행하는 함수를 말한다.

  • 즉시 실행 함수는 단 한 번만 호출되며 다시 호출할 수 없다.

-HOW TO USE❕❓

  • 재귀 함수는 반복되는 처리를 위해 사용한다.
<script>
// 반복문을 사용해 1-10까지 출력하는 함수
function countdonwWithFor(n) {
  for (let i = n; i >=0; i--) console.log(i);
}
countdonw(10)
// 반복문 없이 재귀 함수를 사용해 구현하는 방식
function countdown(n){
  if (n < 0) return; // 인수가 0보다 작아지면 종료
  console.log (n)
  countdown2(n - 1); // 재귀 함수 호출
}
countdonwWithFor(10) // 같은 결과가 나온다.
countdown(10);
</script>
  • 자기 자신을 호출하는 재귀 함수를 사용해 반복되는 처리를 반복문 없이 구현
  • 재귀 함수자신을 무한 재귀 호출한다. 따라서 재귀 함수 내에서 재귀 호출을 멈출 수 있는 탈출 조건을 반드시 만들지 않으면 함수가 무한 호출되어 스택오버 플로 에러가 발생한다.
<script>
// 팩토리얼은 1부터 자기 자신까지의 모든 양의 정수의 곱
// n! = 1 * 2 * ... * (n-1)* n
// 1. 반복문 while로 구현한 팩토리얼
function factorialWithWhile(n) {
  if (n <= 1) return 1;
  let result = n;
  while (--n) result = result * n;
  return result;
}
//
const factorial = function recursiveFuc(n) {
  if (n <= 1) return 1;
  return n * factorial(n-1);
  /*함수 표현식으로 정의한 함수 내부에선 
  함수 이름도 함수를 가리키는 식별자로도 재귀함수를 호출 할 수 있다.*/
}
//단, 함수 외부에선 함수를 호출할 땐 반드시 함수를 가리키는 식별자로 해야한다.
console.log(factorial(0)) // 0! = 1
console.log(factorial(1)) // 1! = 1 
console.log(factorial(2)) // 2! = 2 * 1 = 2
console.log(factorial(3)) // 3! = 3 * 2 * 1 = 6 
console.log((factorialWithWhile(3))) // 같은 결과가 나온다.
</script>

-> 재귀 함수반복문을 사용하는 것 보다 재귀 함수를 사용하는 편이 더 직관적으로 이해하기 쉬울 때만 한정적으로 사용하는 것이 바람직하다.

3. 중첩 함수

-WHAT IS❓

중첩 함수함수 내부에 정의된 함수를 중첩 함수 또는 내부 함수라고 한다
중첩 함수를 포함하는 함수는 외부 함수라 부른다.

-HOW TO USE❕❓

  • 중첩 함수는 자신을 포함하는 외부 함수를 돕는 헬퍼함수의 역할을 한다.
  • 함수 선언문의 경우 if 문이나 for문 등의 코드 블록 내에서도 정의 가능하지만 호이스팅으로 인해 혼란이 발생할 수 있으므로 바람직하지 않다.
  • 중첩 함수스코프와 클로저와 깊은 관련이 있다는 것을 기억하자.
<script>
function outer(){ // 외부 함수
  const x = 1;
  // 중첩 함수, 내부함수
  function inner (){// 외부 함수를 돕는 헬퍼 함수 역할
   // 외부 함수의 변수를 참조할 수 있음
    const y = 3
   console.log(x + y);
  }
  inner()
}
outer()
</script>

4. 콜백 함수

-WHAT IS❓

콜백 함수함수의 매개변수를 통해 다른 함수 내부로 전달 되는 함수이며 고차함수(함수형 프로그래밍 패러다임에서)매개 변수를 통해 함수 외부에서 콜백함수를 전달 받은 함수를 의미한다.

  • 아래 함수들은 공통적으로 반복하는 일을 수행하지만 하는 일의 내용이 다르기에 매번 함수를 새롭게 정의해야하는 단점이 있다.
<script>
// 전달한 인수 n 만큼 일을 반복하는 함수1
function repeat1(n){ // 외부 함수
  // i를 출력
   for(var i = 0; i < n; i++) console.log(i);
}
repeat1(5
// 전달한 인수 n 만큼 일을 반복하는 함수2
function repeat2(n){ // 외부 함수
   for(var i = 0; i < n; i++){
  // i가 홀수 일 때만 출력
  if (i % 2) console.log(i)
    }
}
repeat2(5)
</script>

-HOW TO USE❕❓

  • 콜백 함수를 통해서 함수를 합성을 하면 함수의 변하지 않는 공통 로직은 미리 정의해두고, 경우에 따라서 변경되는 로직은 추상화해서 함수 외부에서 함수 내부로
    전달
    하여 위 코드의 단점을 해결할 수 있다.
<script>
// 외부에서 전달받은 함수 f를 n만큼 반복 호출하는 함수
function repeat(n, f){ // 변경 되는 함수를 f로 추상화하여 외부에서 전달받는다.
   for(var i = 0; i < n; i++) {
   f(i); // i를 전달하면서 함수 f를 호출
  }
}
const logAll = function(i){ // 모든 0부터 n직전까지 출력하는 logAll 함수
    console.log(i)
};
repeat(5, logAll)// 반복 호출할 logAll 함수(콜백함수)를 인수로 전달한다.
const logOdds = function(i){ // 0부터 n직전까지 중 홀수만 출력하는 logOdds 함수
    if (i % 2) console.log(i);
};
repeat(5, logOdds)// 반복 호출할 logOdds 함수(콜백함수)를 인수로 전달한다.
</script>
  • 중첩 함수처럼 콜백 함수 역시 고차함수에 전달되어 헬퍼함수의 역할을 하지만 중첩 함수는 고정되어 교체하기 어렵지만 콜백 함수외부에서 고차함수 내부로 주입할 수 있다.
    -> 따라서 고차함수는 콜백함수를 자신의 일부분으로 합성하여 교체가 보다 유연하다.
  • 고차함수는 매개변수를 통해 전달받은 콜백 함수의 호출 시점을 결정해서 호출한다.
  • 즉, 콜백함수는 고차함수에 의해 호출되며 이때 고차함수는 필요에 따라 콜백함수에 인수를 전달할 수 있다.

<예시1> 콜백함수가 고차 함수 내부에서만 호출 되는 경우

  • 콜백 함수를 익명 함수 리터럴로 정의하면서 곧바로 고차 함수에 전달하면 콜백 함수로서 전달된 함수 리터럴은 고차 함수가 호출 될 때마다 평가되어 함수 객체를 생성한다.
<script>
// 익명 함수 리터럴을 콜백함수로 고차 함수에 전달
function repeat(5, function(i){ // 익명 함수 리터럴은 repeat 함수를 호출 할 때마다 평가되어 함수 객체를 생성한다.
    if (i % 2) console.log(i);
});
</script>

<예시2> 콜백 함수를 다른 곳에서도 호출 할 필요가 있거나, 콜백 함수를 전달받는 함수가 자주 호출되는 경우

  • 함수 외부에서 콜백 함수를 익명 함수 리터럴로 정의한 후 함수 참조를 고차 함수에 전달하면 고차 함수가 호출 될 때마다 콜백 함수가 생성된다.
<script>
//logOdds 함수는 단 한 번만 생성된다.
const logOdds = function(i){ 
    if (i % 2) console.log(i);
};
// 고차 함수에 함수 참조를 전달한다.
repeat(5, logOdds)
</script>

<예시3> 콜백함수는 함수형 프로그래밍 패러다임(고차함수) 외에도 비동기 처리(이벤트, Ajax 통신, 타이머 함수 등)에 활용가능하다.

<script>
// 예시3-1, 콜백 함수를 사용한 이벤트 처리
document.getElementById('btn').addEventListener('click', function(){
	console.log('btn clicked') // btn 버튼 클릭하면 콜백 함수 실행
})
// 예시 3-2.콜백 함수를 사용한 비동기 처리
setTimeout(function{
	console.log('passed one min') // 1초 후 메세지 출력
}, 3000)
</script>

<예시4> 콜백함수는 배열 고차 함수에도 사용된다.

<script>
// 예시 4-1, 콜백 함수를 사용하는 고차 함수 map
var result = [1,2,3].map(function(item) {
	return item * 2;
}) 
console.log(result) // [2, 4, 6]
// 예시 4-2, 콜백 함수를 사용하는 고차 함수 filter
result = [1,2,3].filter(function(item) {
	return item % 2; 
}) 
console.log(result) // [1, 3]
// 예시 4-3, 콜백 함수를 사용하는 고차 함수 reduce
result = [1,2,3].reduce(function(acc, cur) {
	return acc + cur; 
}) 
console.log(result) // 6
</script>

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

-WHAT IS❓

  • 순수 함수함수형 프로그래밍에서 어떤 외부 상태에 의존하지도 않고 변경하지 않는, 부수효과가 없는 함수이다.
  • 반대로 비 순수 함수 외부 상태에 의존하거나 함수의 외부 상태를 변경하는 부수효과가 있는 함수를 의마한다.
<script>
let count = 0;
// 순수 함수 increase는 동일한 인수가 전달되면 언제나 동일 한 값을 반환
function increase(n){
  return ++n;
}
// 순수 함수가 반환한 결과값을 변수에 재할당해서 상태를 변경
let newCount = increase(count);
console.log(count)
count = increase(count);
console.log(count)
let count2 = 0;
// 비순수 함수 increase에 의해 count2에 값이 변화한다.
function increase(){
  return ++count2; // 외부 상태를 직접 참조하여 외부 상태를 변경
}
// 비 순수 함수는 외부 상태 count를 변경하므로 상태 변화 추적이 어렵다
increase();
console.log(count2)
increase();
console.log(count2)
</script>
  • 순수 함수오직 매개변수를 통해 함수 내부로 전달된 인수에게만 의존해 반환값을 만들기 때문에 함수의 외부 상태를 변경하지 않는다.
  • 비 순수 함수외부 상태에 의존하거나 외부 상태를 변경할 수 있다.
    -> 외부 상태를 변경하면 상태 변화를 추적하기 어렵기 때문에 외부 상태 변경을 지양하는 순수 함수 사용하는 것이다 좋다.

*💡conclusion

  • 함수형 프로그래밍은 순수 함수와 보조함수의 조합을 통해 외부 상태를 변경하는 부수 효과를 최대한 억제해 불변성을 지향하는 프로그래밍 패러다임으로 이를 통해 오류를 피하고 프로그램의 안정성을 높일 수 있다.

#📑Study Source

  1. 책 - 딥다이브 자바스크립트
profile
자기 신뢰의 힘을 믿고 실천하는 개발자가 되고자합니다.

0개의 댓글