관리 메뉴

흰둥씨의 개발장

[자바스크립트] 딥다이브) 함수 본문

BoOk/JS deep dive

[자바스크립트] 딥다이브) 함수

돈워리비해삐 2023. 5. 24. 05:50

1) 함수 

ㄴ수학의 함수처럼 입력(input)을 받아 출력(output)을 내보내는 일련의 과정

ㄴ프로그래밍 언어에서는 일련의 과정을 문(statement)으로 구현하고 코드블록으로 감싸서 하나의 실행단위로 정의한 것

매개변수(parameter) : 함수 내부로 입력을 전달 받는 변수 

인수(argument) : 입력

반환값(return value) : 출력

ㄴ함수는 값이고, 여러개 존재할 수 있어, 특정 함수를 구별하기 위해 식별자인 함수 이름을 사용 (함수역할 파악할수 있게 ; 가독성높이기)

ㄴ함수정의만으로 함수 실행되지 않고 함수 호출해야함 

함수호출 (function call/ invoke) : 인수를 매개변수를 통해 함수에 전달하여 함수 실행을 명시적으로 지시해야 함

ㄴ함수를 사용하는 이유는 '코드 재사용'측면에서 유용하기 때문 (유지보수편의증가, 코드 신뢰성높이는 효과) 

//함수선언문을 통해 함수정의 (함수 선언문은 표현식이 아닌 문=> undefined출력됨, 함수명 생략불가)
//그룹연산자()내에 있는 함수 리터럴은 함수리터럴 표현식으로 해석됨 ->호출안되는 형태 (function hi(x){return x});
function addcalc(x, y) {
	return x + y ;
  }

let addResult = addcalc(2, 5);  // 함수호출

//함수 표현식을 통해 함수정의 
let divCalc = function (x, y){
	return x / y;
  };
  
//함수 생성자를 통해 함수정의
let minusCalc = new Function('x', 'y', return x - y);

//화살표 함수(ES6)
let modCalc = (x, y) => x % y;

  - 1) 함수 리터럴

ㄴ함수는 객체 타입의 값 (함수리터럴은 function키워드 + 함수이름 + 매개변수 목록 + 함수 몸체로 구성)

ㄴ일반 객체는 호출할수 없음 , 함수는 호출가능 

ㄴ자바스크립트 엔진은 함수를 호출하기위해 함수이름과 동일한 이름의 식별자를 암묵적으로 생성하고 거기에 함수 객체를 할당함

ㄴ함수는 함수 이름이 아닌 함수 객체를 가리키는 식별자로 호출되는 것

 

  - 2) 함수 표현식 

ㄴ값의 성질을 갖는 객체 = 일급 객체 (함수를 값처럼 자유롭게 사용할 수 있다)

ㄴ익명함수 : 함수 이름 생략한 것 

함수선언문은 표현식이 아닌 문

함수표현식은 표현식인 문 

 

  - 3) 함수생성시점과 함수 호이스팅 (*런타임: 코드가 한줄씩 순차 실행되는 시점)

함수선언문으로 정의한 함수는 함수 선언문 이전에 호출할 수 있다. 

ㄴ함수선언문은 런타임 이전에 함수 객체가 먼저 생성되고, 자바스크립트 엔진이 함수이름과 동일한 식별자를 생성해 함수객체를 할당함 

ㄴ그렇기 때문에 함수 선언문이 코드 선두로 끌어올려진것 처럼 동작하는 '함수 호이스팅' 이 발생

함수표현식으로 정의한 함수는 함수 표현식 이전에 호출할 수 없다. => 함수 표현식 사용을 권장

ㄴ함수표현식은 변수에 할당되는 값이 함수 리터럴인 문

ㄴ변수 할당문의 값은 할당문이 실행되는 런타임에 평가되기 때문에 '변수 호이스팅'이 발생

ㄴ함수표현식 이전에 함수를 참조하면 undefined 평가됨 

 

  - 4) 함수 생성자 

ㄴ일반적이지도 않고 바람직하지도 않음 

ㄴ클로저를 생성하지 않구 별로임

 

  - 5) 화살표 함수 (ES6)

ㄴ익명함수로 정의함

ㄴ기존 함수 보다 내부동작도 간략화 되어있음  

ㄴ기존함수와 this 바인딩 방식 다르고, prototype프로퍼티가 없고, arguments 객체를 생성하지 않음 

 

 

2) 함수 호출

ㄴ함수를 가리키는 식별자와 한쌍의 소괄호인 함수 연산자()로 호출

ㄴ함수 호출 연산자() 내에는 0개 이상이 인수를 쉼표로 구분하여 나열

ㄴ매개변수(parameter)를 통해 인수(argument)를 전달 (인수는 값으로 평가될 수 있는 표현식이어야 함)

ㄴ매개변수는 함수를 정의할 때 선언하고, 함수내 변수와 동일하게 취급됨(undefined으로 초기화된 이후 인수가 순서대로 할당됨)

ㄴ매개변수는 함수 내부에서만 참조가능 (외부참조안됨)

ㄴ매개변수의 개수와 인수의 개수가 일치하지 않더라도 에러가 나지는 않음 (인수가 부족해서 할당되지 않은 값은 undefined)

ㄴ매개변수보다 인수가 더 많으면 초과된 인수는 무시됨

function add (a, b) {
	return a + b; 
  }
  
console.log(add(2)); // 2 + undefined = NaN
console.log(add(2, 3, 4)); // 2 + 3 = 5
console.log(add('짱구는 ', '못말려'));  //짱구는 못말려

  - 1) 인수 확인 

ㄴ코드에서 어떤 타입의 인수를 전달해야 하는지, 어떤 타입의 값을 반환하는지 명확하지 않음

ㄴ자바스크립트 함수는 매개변수와 인수의 개수가 일치하는지 확인하지 않음

ㄴ자바스크립트는 동적 타입 언어이다. 그래서 자바스크립트 함수는 매개변수의 타입을 사전에 지정할 수 없다. 

ㄴ그래서 아래와 같이 확인하거나,

ㄴ매개변수에 기본값 넣기, 단축평가를 활용하면 인수에 undefined이 대입되지 않게 하거나,

ㄴ타입스크립트, 정적타입선언할 수 있는 자바스크립트 상위 확장 도입해서 컴파일시점에 부적절한 호출을 방지하거나 할 것 

function addCalc(a, b) {
	if(typeof a !== 'number' || typeof b !== 'number'){
    throw new TypeError('인수는 모두 숫자값이어야 함');
    }
   return a + b; 
  }
  
console.log(addCalc(2));         //TypeError: 인수는 모두 숫자값이어야 함
console.log(addCalc('x', 'y'));  //TypeError: 인수는 모두 숫자값이어야 함

 

  - 2) 매개변수의 최대개수

ㄴ명시적 제한은 없으나, 이상적인 함수는 한가지 일만 하고, 가급적 작게 만들도록, 매개변수는 최대 3개 이상 넘지 않도록 권장 

ㄴ3개이상의 매개변수 필요시 하나의 매개변수에 객체로 인수를 전달하는것이 유리함 

 

  - 3) 반환문

ㄴreturn 키워드와 표현식(반환값)으로 이루어짐 

ㄴreturn 키워드가 반환한 표현식의 평가결과(=반환값)를 반환

ㄴ반환문은 함수실행을 중단하고 함수안을 빠져나감(반환문 이후 다른 문은 실행되지 않고 무시됨)

ㄴ반환값으로 사용할 표현식을 명시적으로 지정하지 않으면(생략하면) undefined

ㄴreturn 키워드와 반환할 표현식 사이 줄바꿈 있으면 이상해짐(세미콜론 자동삽입기능때문에 표현식이 무시됨)

ㄴreturn 키워드는 함수내에서만 사용가능_함수 밖에서 쓰면 문법에러남 ( Node.js에서는 파일의 바깥영역에 반환문 써도 에러 안남 )

 

3) 참조에 의한 전달과 외부상태 변경

ㄴ객체는 참조에 의한 전달방식으로 동작해서 원본이 훼손될수 있음

ㄴ객체의 변경을 추적하려면 observer패턴 등을 통해 ...쉽지않음 

ㄴ위 문제를 해결하기 위해서는 객체를 불변객체로 만드는 것 => deep copy를 해서 사용하는 것

 

4) 다양한 함수의 형태

  - 1) 즉시 실행 함수 (IFE, Immediately invoked function Expression)

ㄴ단 한번만 호출되고 다시 호출 할 수 없음

ㄴ익명함수를 사용하는 것이 일반적

ㄴ그룹연산자()안의 기명함수는 선언함수가 아닌 함수리터럴로 평가됨 

ㄴ값반환, 인수전달 가능 

(function foobao(){ //그룹연산자 안의 기명함수 = 함수리터럴
let a = 1;
let b = 2;
return a * b;
}());

foobao(); //referenceError

console.log(typeof (function Ibao(){})); //function
console.log(typeof (function (){}));	 //function

 

  - 2) 재귀함수 (recursive function)

ㄴ재귀호출을 수행하는 함수

재귀호출? 함수가 자기 자신을 호출하는 것

ㄴ자신을 무한 재귀 호출하기 때문에 탈출 조건을 반드시 만들어야 함 (주의!)

ㄴ탈출조건 없으면 stack overflow Error남 

function count (x) {
 for (let = i = x; i >= 0; i--) console.log(i);
 }
 
//위 함수를 for문 없이 재귀 함수를 이용해서 구현
function recCount (x) {
	if (x < 0) return;
    console.log(x);
    recCount(x - 1);  //재귀호출
  }
  
//팩토리얼을 재귀함수로 간단히 구현하기 
function factorialCalc (x){
	if (x <= 1) return 1; // x가 1이하일때 1반환하고 탈출, 그게 아니면 아래 return 문 수행
    
    return x * factorialCalc(x - 1); // 재귀호출
  }
   
console.log(factorialCalc(0)); //0! =1
console.log(factorialCalc(1)); //1! =1
console.log(factorialCalc(2)); //2! = 2 * 1 = 2
console.log(factorialCalc(3)); //3! = 3 * 2 * 1 = 6 
console.log(factorialCalc(4)); //4! = 4 * 3 * 2 * 1 =24

 

  - 3) 중첩 함수(nested function = inner function)

ㄴ중첩함수 = 내부함수 = 함수 내부에 정의 된 함수

ㄴ외부함수 = 중첩함수를 포함하는 함수

ㄴ중첩함수는 외부함수의 내부에서만 호출가능 

ㄴ중첩함수는 외부함수를 돕는 헬퍼함수 역할함

 

  - 4) 콜백함수(callback function)

ㄴ함수의 매개변수를 통해 다른 함수의 내부로 전달되는 함수, 고차함수를 돕는 헬퍼함수역할함

고차함수(higher-order func) ? 매개변수로 함수를 전달 받은 함수 , 콜백함수를 자신의 일부분으로 합성함 

ㄴ고차함수는 필요에 따라 콜백함수에 인수 전달 가능 

//콜백함수를 사용한 이벤트 처리 
document.querySelctor('.button').addEventListener('click', ()=>{
	console.log('대추나무 버튼 눌렷네');
  });
  
//콜백함수를 사용한 비동기처리
setTimeout(()=>{
	console.log('1초 지나감');
  },1000);
  
//콜백함수를 사용하는 map
let resp1 = [1,2,3].map((item)=>{
	return item * 2;
  });
console.log(resp1) // [2,4,6]

//콜백함수를 사용하는 filter
let resp2 =[1,2,3].filter((item)=>{
	return item % 2;
  });
console.log(resp2) // [1,3]

//콜백함수 사용하는 reduce
let resp3 =[1,2,3].reduce((acc, curr)=>{
	return acc + curr;
  }, 0);
console.log(resp3) // 6

 

  - 5) 순수 함수와 비순수 함수 

* 순수(pure)함수? 외부 상태에 의존하지 않고, 외부상탤르 변경하지도 않는, 부수효과(side effect)가 없는 함수

ㄴ동일한 인수가 전달되면 언제나 동일한 값을 반환함 = 함수로 전달된 인수에만 의존해 값을 반환

ㄴ인수를 변경하지 않는 것이 기본, 인수의 불변성 유지

let number = 0;

function increase (x) {
	return ++x;
    }

number = increase(number);
console.log(number); // 1

number = increase(number);
console.log(number); // 2

 

* 비순수(impure)함수? 외부상태에 의존하거나, 외부상태를 변경하는, 부수효과가 있는 함수

ㄴ지양하기

let num = 0;

function increase() {
	return ++num;  //외부 상태에 의존하여 외부상태 변경함
  }
  
increase();
console.log(num); // 1

increase();
console.log(num); // 2