본문 바로가기
3. 웹개발/3_2_1 Javascript

[JavaScript (6)] Javascript 함수(함수 기본, 재귀함수, 호이스팅 등)

by 갓대희 2020. 3. 20.
반응형

[JavaScript (6)] Javascript 함수(함수 기본, 재귀함수, 호이스팅 등)

안녕하세요. 갓대희 입니다. 이번 포스팅은 [ 자바스크립트 함수 입니다. : ) 

 

0. 들어가기 앞서

 

함수의 정의, 호출방법, 그리고 활용 방법 등을 간단히 알아보려 한다.

 

1. 함수(function) 기본

▶ 1. 함수(function)란?

 - 특별한 목적의 작업을 수행하도록 설계된 독립적인 블록
 - javascript에서 코드 집합을 나타내는 자료형)
 - 작업을 수행하거나 값을 계산하는 문장 집합 같은 자바스크립트 절차
 - 함수도 일반 객체처럼 값으로 취급된다. 즉 하나의 데이터 타입 이다.
   이러한 특징때문에 자바스크립트의 함수는 일급 객체라고 하며, 변수에도 할당 가능하다.

※ 참고할 용어
◎ 일급시민(First-Class Citizen) : 다음을 충족하는 값
    1) 변수(variable)에 담을 수 있다.
    2) 함수의 인자(parameter)로 전달할 수 있다.
    3) 함수의 반환값(return value)으로 전달할 수 있다.
◎ 일급객체(First-Class-Object) : 일급시민의 조건을 충족하는 객체
◎ 일급함수(First-Class-Function) : 일급시민의 조건을 충족하는 함수

 

▶ 2. 함수의 구성

1) 함수명
 - 함수명은 함수 내부 코드에서 자신을 재귀적으로 호출하거나 자바스크립트 디버거가 해당 함수를 구분하는 식별자로 사용 한다.
 - 다만 함수명은 선택사항. 이때 함수명이 없는 함수를 익명 함수라 한다.
2) 매개변수(parameter)
 - 함수의 정의에서 전달받은 인수를 함수 내부로 전달하기 위해 사용하는 변수.
 - 매개변수는 C, Java 언어와 같은 기존 언어의 함수 매개변수 형태와 거의 비슷하지만, 변수 타입을 기술하지는 않는다.
3) 실행문
4) 반환문(Option)
 - 반환문은 함수의 실행을 중단하고, return 키워드 다음에 명시된 표현식의 값을 호출자에게 반환한다.
 - 반환시 리턴 타입 제한은 없다. 배열이나 객체를 포함한 모든 타입의 값을 반환할 수 있다.

▶ 3. 함수 생성 방법

1) 함수 선언문 방식
 - 함수명 정의가 필수이다.

function 함수명(매개변수1, 매개변수2,...) {
    실행문;
    // 반환문; (optional)
}

ex)

function add(x, y){
	return x+y;
}
console.log(add(3, 4));// 7

 

2) 함수 표현식 방식
 - 함수 리터럴로 하나의 함수를 만들고, 여기서 생성된 함수를 변수에 할당하여 함수를 생성하는 것을 함수 표현식 이라고 한다.

 

2.1) 익명 함수 표현식(이름 없는 함수 표현식)

var 변수명 = function(매개변수1, 매개변수2,...) {
    실행문;
    // 반환문; (optional)
}

ex)

var addNoNamed = function(x, y){ // 이름 없는 함수형태 => 익명 함수 표현식
    return x+y;
};
console.log(addNoNamed(3,4)); // 7

 

2.2) 기명 함수 표현식(이름 있는 함수 표현식)

var 변수명 = function 함수명(매개변수1, 매개변수2,...) {
    실행문;
    // 반환문; (optional)
}

ex)

var addNamed = function sum(x, y){ // 이름 포함된 함수 표현식 => 기명 함수 표현식
    return x+y;
};
console.log(addNoNamed(3,4)); // 7
console.log(sum(3,4)); // Uncaught ReferenceError : sum is not defined 에러 발생

 - 위의 예제에서 보면 함수 표현식에서 사용된 함수 이름이 외부 코드에서 접근 불가능 하기 때문에 오류가 발생 하였다.
 - 함수 표현식에 사용된 함수 이름은 정의된 함수 내부에서 해당함수를 재귀적으로 호출하거나, 디버거 등에서 함수를 구분할 때 사용된다.

 

※ 1. 함수 선언문, 2.함수 표현식의 세미콜론

 - 1. 함수 선언문, 2.함수 표현식을 살표 보았는데 비슷하지만, 미묘 하게 다른 부분이 있었을 것이다.  => 끝에 세미콜론이 있고 없고의 차이다.
 - 자바스크립트는 세미콜론 사용을 강제하지는 않는다. 자바스크립트 인터프리터가 자동으로 세미콜론을 삽입시켜 주기 때문이다.
 - 기본적으로 함수 선언문에서는 ";"을 붙이지 않는다.
 - 함수 표현식에서는 ";"을 붙인다.(변수 선언할때 보면 세미콜론을 붙였을 것이다.)

   ex)var 변수 = 1;
 - 다음 예제를 먼저 한번 실행하여 보자.

var testFunc = function(){ 
    return 1;
} //세미콜론 사용하지 않음. 
(function() { 
    console.log("test"); 
})();


 - 에러가 발생한다. 자바스크립트 파서가 testFunc()의 함수 정의에서 세미콜론을 사용하지 않아, return 1; } 이후 함수가 끝났다고 판단하지 않기 떄문이다.
 - 이 부분은 이해할 필요는 없을 것 같다. 아무튼 세미콜론을 어떤 문법에 따라 잘 써준다면, 오류도 발생하지 않고 유용하다.
 - 많은 자바스크립트 가이드에서 함수 표현식 방식에서의 세미콜론 사용을 강력 히 권고한다.

 

3) Function() 생성자 함수를 통한 함수 생성

new Function (arg1, arg2, ... argN, functionBody)

 - arg1, arg2, .., argN : 함수의 매개변수
 - functionBody : 함수가 호출될 때 실행될 코드를 포함한 문자열

ex)

var add = new Function('x', 'y', 'return x + y');
console.log(add(3,4)); //7

▶ 4. 함수 호출 방법

 - 예를 통해 살펴 보자.

ex)

ex)
function add(x, y){
	return x+y;
}
var result = add(3, 4); // add()함수를 호출하며 파라미터를 x,y에 대입한다. 결과7을 result에 대입니다.
console.log(add(3, 4));// 7

▶ 5. 함수의 유효 범위(function scope)

 - 자신이 정의된 범위 안에서 모든 변수 및 함수에 접근할 수 있다. 
 - '전역 함수'는 모든 전역 변수와 전역 함수에 접근할 수 있다.
 - '내부 함수'는 그 함수의 부모 함수에서 정의된 모든 변수 및 부모 함수가 접근할 수 있는 모든 다른 변수까지도 접근할 수 있다.
 - 변수의 유효 범위도 함수의 유효 범위랑 비슷하게 보면 된다. 이 부분은 예제를 통해 살펴 보자

ex)

// 전역 변수 선언. 어디에서도 접근 가능.
var a = 3, b = 4;

// sub()를 전역 함수로 선언함.
function add() {
	return a + b; // 전역 변수인 a, b에 접근 가능
}
console.log(add()); // 7

function parentAdd() {
    var x = 1, y = 2; // 전역 변수와 같은 이름으로 선언.
    function add2() {  // add() 함수는 내부 함수로 선언.
        return x + y; // 전역 변수가 아닌 지역 변수 a, b에 접근함.
    }
    return add();
}

console.log(parentAdd()); // 3
console.log(add2()); // Uncaught ReferenceError: add2 is not defined
console.log(a) // 3
console.log(x) // Uncaught ReferenceError: x is not defined

▶ 6. 함수 호이스팅(hoisting)

 - 예를 먼저 살펴보자.
ex)

console.log(add(2,3)); //5
// 함수 선언문
function add(x, y){
     return x + y;
}
console.log(add(3, 4)); //7

 - "함수 선언문" 정의 이전에 함수를 호출해도 5라는 결과가 노출된다.
 - 즉 "함수 선언문" 형태로 정의한 함수의 유효범위는 코드의 맨 처음부터 이다. 이를 "함수 호이스팅" 이라고 한다.
 - 이와 같은 이유로 함수 표현식을 권장 하고 있다.
 - 위의 예제를 "함수 표현식" 형태로 정의하고 다시 호출해 보자.
ex)

add(2,3); // uncaught type error
var add = function (x, y){
	return x + y;
}

 - "함수 표현식" 형태로 정의하게되면 호이스팅이 일어나지 않는다.

▶ 7. 매개변수(parameter), 인수(argument)

1) 매개변수(parameter)
 - 함수의 정의에서 전달받은 인수를 함수 내부로 전달하기 위해 사용하는 변수.
 - 매개변수는 C, Java 언어와 같은 기존 언어의 함수 매개변수 형태와 거의 비슷하지만, 변수 타입을 기술하지는 않는다.
 - 자바스크립트의 경우는 함수를 호출할 때 함수의 정의보다 적은 수의 인수가 전달되더라도, 오류를 발생시키지 않는다.
   이 경우 전달되지 않은 나머지 매개변수에 자동으로 undefined 값을 설정한다.

2) 인수(argument)

 - 함수가 호출될 때 함수로 값을 전달해주는 값을 말한다.
ex) 매개변수, 인수

function add(x, y){ // x, y 2개의 매개변수를 가지는 함수 정의
	return x+y;
}

add(3, 4);	// 인수로 3, 4를 전달. 결과 : 7
add(3);		// 인수로 3 전달. y에는 undefined 값이 설정됨.  결과 : 3 + undefined = NaN(계산할수 없음)
add();		// 인수로 아무것도 전달하지 않고 함수 호출. 결과 : undefined + undefined = NaN(계산할수 없음)

 

3) arguments 객체
 - 함수의 정의보다 더 많은 수의 인수가 전달된 경우, 해당 인수들을 참조할 수 없다.
 - 이럴때 사용할 수 있는 것이 arguments 객체이다.
 - 활용 방법은 예제를 통해 확인하면 좋을 것 같다.
ex)

function add(){
	var sum = 0;
	for (var i=0; i<arguments.length; i++){
		sum += arguments[i];
	}
	return sum;
}

console.log(add(3, 4));	// 7
console.log(add(3));	// 3
console.log(add());     // 0

 

4) 디폴트 매개변수(default parameter)
 - 함수를 호출할 때 명시된 인수를 전달하지 않았을 경우에 사용하게 될 기본값
 - 현재 모든 브라우저에서 지원하지 않기 때문에 추천하지 않는다.
 - 선언하지 않은 경우엔 undefined
ex)

function add(a, b = 0) { // b에 해당하는 인수가 없으면 디폴트 0으로 설정한다.
    return a + b;
}
add(3, 4);	// 7
add(3);		// 3
add();		// NaN

 

5) 나머지 매개변수(rest parameter)
 - 생략 접두사(...)를 사용하여 특정 위치의 인수부터 마지막 인수까지를 한 번에 지정할 수 있다.
ex) 첫 번째 인수에 값을 저장하고, 생략 접두사를 통해 모든 값을 빼보자.

function minusFunc(firstNum, ...restArguments){
	for(var i = 0; i < restArguments.length; i++) {
        firstNum -= restArguments[i];
    }
    return firstNum;
}
var result = minusFunc(20, 1,2,3,4,5);
console.log(result); // 5

 

2. 함수(function) 심화

▶ 8. 재귀함수

 - 자기 자신을 호출하는 함수를 재귀함수 라고 한다.
 - 위에서 설명했듯이, 함수는 자신을 참조, 호출 가능하다.
ex)

function factorialTest(x) {
	console.log("현재 x : " + x);
	var y = 1;
	if(x==y) return y;
	else return x*factorialTest(x-1); // 재귀 호출
}
var result = factorialTest(3);
console.log("결과 : " + result); // 6

▶ 9. 클로저

 - 함수가 실행 후 종료 되어도, 종료된 함수 내의 변수가 소멸되지 않는 특성 이다.
 - 이런 특성을 활용한 함수가 클로저 함수 이다.
 - 리턴문이 포함된 함수여야 한다.
 - 예제를 통해 먼저 확인 해 보자.
ex)

function outerFunc(a, b) {
	var c = 10;
	function innerFunc(x) {
		return (x * x) * c;
	}
	return innerFunc(a) + innerFunc(b);
}

var a = outerFunc(2, 3); // 130

console.log(a); //130
console.log(innerFunc(3)); // Uncaught ReferenceError: innerFunc is not defined
console.log(c); // Uncaught ReferenceError: c is not defined

 - 외부 함수는 내부 함수의 인수와 변수를 사용할 수 없는 반면에, 내부 함수는 외부 함수의 인수와 변수를 사용할 수 있다.

▶ 10. 즉시 실행 함수

 - 익명 함수를 만들면서 즉시 실행 하는 함수
 - 플러그인이나 라이브러리 등을 만들 때 많이 사용 한다.
 - 기본 문법

(function () {
    // statements
})()

1) 기명 즉시 실행 함수

ex) 다음 두 예제는 똑같은 의미의 기명 함수

(function square(x) {
	console.log(x*x);
})(2);

(function square(x) {
	console.log(x*x);
}(2));

 

2) 익명 즉시 실행 함수
ex) 다음 두 예제는 똑같은 의미의 익명 함수

(function (x) {
	console.log(x*x);
})(2);

(function (x) {
	console.log(x*x);
}(2));

 

3) 변수에 즉시 실행 함수 저장 하기
 - 함수 이기 때문에 변수에 저장 할 수 있다.
 - 이 경우에는 즉시 실행 함수임에도 재사용 가능하다.
ex)

ex)
(square = function (x) {
    console.log(x*x);
})(2);
square(2);

 

4) 라이브러리 전역 변수 충돌 해결
 - jQuery와 prototype.js 또는 MooTools 등 다양한 라이브러리를 사용하다보면, $라는 전역변수가 충돌하는 경우가 있다.
   다른 라이브러리에서 $를 재정의하면 jQuery에서 정상동작하지 않을 수 있는데, 이럴때 즉시실행 함수를 사용하여 해결 가능하다.
방법1)

(function ($) {
	// You can use the locally-scoped $ in here as an alias to jQuery.
})(jQuery);

 

방법2)

jQuery( document ).ready(function( $ ) {
    // You can use the locally-scoped $ in here as an alias to jQuery.
});

▶ 11. this

 - 자바스크립트의 경우 함수 호출 방식에 의해 this에 바인딩할 어떤 객체가 동적으로 결정된다.
 - this는 매우 중요하지만, 이해하기 어려운 것 같다. 일단 이번엔 함수에서의 간단한 this의 의미를 알아 보고 
   this에 대해 자세히 다음 기회에 알아 보도록 한다.

1) 일반함수의 this
 - 함수 내부의 this는 전역 객체 window를 가리킨다.
ex)

function sum(a, b) {  
   console.log(this === window); // => true, 즉 this는 전역 객체이다.
   
   this.result = a + b; // 전역 객체에 'result'라는 속성을 추가 한것과 같다.
   return this.result;
}
console.log(sum(3, 4));     // => 7
window.result; // => 7

 

2) 내부함수의 this
 - 외부 함수에서의 this와 내부 함수에서의 this는 전혀 다르다.
ex)

var numbers = {  
	a: 3,
	b: 4,
	sum: function() {
		console.log(this === numbers); // => true
			function calculate() {
				// this는 window
				console.log(this === numbers); // => false
			return this.a + this.b;
			}
		return calculate();
   }
};
numbers.sum(); // NaN

 - 주석을 보지 않고 생각해보면 마치 3+4인 7이 나올꺼 같지만 NaN이라는 결과가 나온다.
 - 이런 문제를 해결하는 방법을 확인해 보자.

 

해결법1) .call 메소드를 사용

var numbers = {  
	a: 3,
	b: 4,
	sum: function() {
			function calculate() {
				// this는 window
			return this.a + this.b; // => numbers.numberA + numbers.numberB 와 동일한 결과
			}
		return calculate.call(this);
   }
};
numbers.sum(); // 7

 

해결법2) this를 변수에 넣어 내부함수에서 사용

var numbers = {  
	a: 3,
	b: 4,
	sum: function() {
		var objThis = this;
			function calculate() {
				// this는 window
			return objThis.a + objThis.b; // => numbers.numberA + numbers.numberB 와 동일한 결과
			}
		return calculate();
   }
};
numbers.sum(); // 7

 

 - this는 정말 복잡하다. 일단 this는 따로 포스팅하고 일단 간단한 활용법만 정리하였다.

▶ 12. 화살표 함수(Arrow function)

 - function 키워드 대신 화살표(=>)를 사용하여 보다 간략한 방법으로 함수를 선언할 수 있다. 
 - 물론 모든 함수가 가능한것은 아니다.

 - 매개변수 지정 방법
() => { ... } // 매개변수가 없을 경우
   (x) => { ... } // 매개변수가 한 개인 경우(소괄호를 생략 가능)
(x, y) => { ... } // 매개변수가 여러 개인 경우(소괄호를 생략 불가)

 - 함수 몸체 지정 방법
x => { return x + y }  // 함수 몸체가 한줄 구문인 경우
x => x + y             // 함수 몸체가 한줄의 구문인 경우(중괄호를 생략 가능, 암묵적으로 return)

(x) => { // 함수 몸체가 여러줄 구문인 경우
  var y = 2;
  return x * y;
};

 - 객체 반환시에는 소괄호를 사용한다.
() => { return { x : 2 }; }
() => ({ x : 2 })

 - 익명함수로만 사용 가능하다.
 - 참고로 var대신 const로 선헌 하였는데, 이런 경우 변수 재선언, 재할당 모두 불가능하다.

const add = (x,y) => x + y;
console.log(add(3,4)); // 7

const add = (x,y) => x + y; // Uncaught SyntaxError: Identifier 'add' has already been declared

 

반응형

댓글0