함수 내 함수 호출 - hamsu nae hamsu hochul

함수를 리턴하는 함수

함수도 일급 객체이므로 일반 값처럼 함수 자체를 리턴할 수 있다. 이러한 특징을 활용하여 함수를 호출함과 동시에 다른 함수로 바꾸거나, 자기 자신을 재정의하는 함수를 구현할 수 있다.

var self = function(){ console.log('a'); return function(){ console.log('b'); } } self = self(); // a self(); // b

처음 self() 함수가 호출됐을 때는 'a'가 출력된다. 그리고 다시 self 함수 변수에 self() 함수 호출 리턴값으로 내보낸 함수가 저장된다.

두번째로 self() 함수가 호출됐을 때는 'b'가 호출된다. 즉, self() 함수 호출 후에, self 함수 변수가 가리키는 함수가 원래 함수에서 리턴 받은 새로운 함수로 변경됐기 때문이다.

[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

Toplist

최신 우편물

태그