본문으로 바로가기

JS Basics #2 - Function

category Study Group/javaScript 기초 2019. 9. 4. 12:18

함수 파트에서는 함수와 관련된 다음의 다양한 개념에 대해 자세히 알아보도록 하겠습니다.

  • 함수 생성과 스코프(scope)
  • 변수 호이스팅(hoisting), 유효범위(scope)
  • 함수 객체
  • 다양한 함수 형태
  • 함수 호출과 this
  • 프로토타입과 프로토타입 체이닝

 

 

함수 생성하기

자바스크립트에서 가장 중요한 개념 1순위는 단연 함수입니다.
자바스크립트에서의 함수는 언뜻 보면 다른 프로그래밍 언어와 마찬가지의 기능을 제공하고 있습니다.
즉, 특정 기능을 제공하는 코드를 작성해서 함수를 정의하고, 이를 호출해서 결과값을 얻는 것처럼 말입니다. 하지만 이러한 기능 외에도 자바스크립트의 함수는 모듈화 처리나 클로저, 객체 생성 등 자바스크립트의 근간이 되는 많은 기능을 제공하고 있습니다.

 

함수 정의

자바스크립트에서 함수를 생성하는 방법은 3가지가 있습니다.
이들 방식은 모두 같은 함수를 생성하지만, 각각의 방식에 따라 함수 동작이 미묘하게 차이가 납니다.

  • Function() 생성자 함수( =Function() 객체)
  • 함수 선언문(function statement)
  • 함수 표현식(function expression)

 

 

#1-1 Function() 생성자 함수를 통한 함수 생성하기

자바스크립트의 함수도 Function() 이라는 기본 내장 생성자 함수로부터 생성된 객체라고 볼 수 있습니다.

함수는 괄호()를 사용해 호출할 코드 문장을 모아두는 곳이며, 함수를 실행할 때 괄호 안에 매개 변수(parameter)를 전달할 수 있어 함수 안의 문장에서 특정한 값에 접근하도록 만들 수 있습니다.

Fucntion() 생성자 함수로 함수를 생성하는 문법은 다음과 같습니다.

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

 

위 문법을 사용해서 작성한 add() 함수는 다음과 같습니다.

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

 

 

#1-2 Function() 매개변수

Function() 생성자는 매개변수를 무한정 가질 수 있지만, 마지막 매개변수는 항상 함수의 몸체를 구성할 코드를 나타내는 문자열이어야 합니다. 생성자에 전달하는 그 밖의 매개변수는 함수 안에서 사용할 수 있으며 여러 매개변수를 전달할 때는 매개변수 사이를 쉼표로 구분해야 합니다.

js
var calc = new Function('x, y, z', 'return (x + y) * z');
console.log( calc( 3, 4, 2 ) ); // 14

위와 같은 Function() 생성자 함수를 사용한 함수 생성 방법은 자주 사용되지 않기 때문에 이러한 문법은 실제 사용하기 보다는 다른 사람이 작성한 소스를 분석할 때 나오는 경우기 있을 수 있기 때문에 상식수준으로 알아두면 좋을 것입니다.
그렇다면 Function() 생성자 함수보다 자주 사용되는 문법은 무엇일까?!
아마도 자바스크립트를 조금이라도 접해 본 사람이라면 함수 선언문, 함수 표현식을 사용하고 있을 것입니다.
앞서 배운 객체 리터럴 방식으로 일반 객체를 생성할 수 있는 것처럼, 자바스크립트에서는 함수 리터럴을 이용해 함수를 생성할 수 있습니다.
즉, 함수 리터럴 역시 객체 리터럴과 마찬가지로 자바스크립트의 단축 표기법으로 함수를 쉽게 정의할 수 있는 문법을 제공하는 것입니다. 실제로 함수 선언문이나 함수 표현식 방법 모두 이런 함수 리터럴 방식으로 함수를 생성합니다.

 

 

함수 리터럴

앞서 Function() 생성자를 이용하여 생성했던 함수를 함수 리터럴를 통해 아래와 같이 정의할 수 있습니다.

js
function add(x, y) {
    return x + y;
}

add(3,4);

// 함수 리터럴은 위 예제를 참고할 경우 다음과 같이 구성되어 있다.
// ① function 키워드
// ② 함수명 : add
// ③ 매개변수 리스트 : x, y
// ④ 함수 몸체 : 중괄호 {}
// ⑤ 반환 : return
// ⑥ 함수호출 : add();
// ⑦ 인자(argument) : 3, 4
  1. function 키워드 : 자바스크립트 함수 리터럴은 function 키워드로 시작합니다.
  2. 함수명 : 함수명은 함수 몸체의 내부 코드에서 자신을 재귀적으로 호철하거나 또는 자바스크립트 디버거가 해당 함수를 구분하는 식별자로 사용됩니다. 여기서 주목할 점은 함수명은 선택 사항이라는 점입니다. 만약 함수명이 없는 함수를 정의한다면 이를 익명함수라고 합니다.
    다시 말해, 함수명이 있는 것을 기명 함수(=기명함수 표현식)이라고도 표현하고, 이름을 생략한 함수를 익명 함수(=무명함수, 무명 함수 표현식) 이라고 부르기도 합니다.
    바로 다음에 배울 함수 표현식이 바로 익명함수를 그냥 간단하게 함수 표현식(function expression)이라고 부르고 있습니다.
  3. 매개변수 리스트 : 이 매개변수는 함수를 실행할 때 어떤 값을 함수 몸체로 보낼수 있는 이동 수단이며, 매개변수(parameter)는 타입을 기술하지 않습니다. 즉, 함수를 실행시 어떤 값을 함수 몸체에서 사용하기 위한 값을 함수를 호출하는 부분의 인자(argument)값을 받는 매개체 역할을 합니다.
  4. 함수 몸체 : 실제 함수가 호출됐을 때 실행되는 코드 부분입니다.
  5. return(반환) : 함수 몸체에서 실행되는 부분 중에 사용자가 반환하고자 하는 값을 전달하는 경우에 사용합니다.
    return 은 선택사항이지만 return 문을 생략할 경우에는 일반 함수에서 undefined 를 반환하고 new 키워드를 사용한 생성자 함수 경우에는 this 를 반환합니다.
    다시말해, 함수에 return 문을 사용하든 사용하지 않든 간에 함수는 항상 값을 반환한다는 사실을 기억해야 합니다.
  6. 함수호출 : 함수를 정의했다면 함수를 실행하기 위해 호출해줘야 합니다.
    일반적인 함수 호출은 위와 같으며, 다양한 호출 패턴이 있는데 이는 추후에 다뤄보도록 하겠습니다.
  7. 인자(argument) : 함수를 호출하는데 있어서 함수 기능에 필요한 값이 있다면 타입(type)에 상관없이 어떤 타입값이라도 전달해 줄 수 있습니다.

 


#2 함수 선언문 방식으로 함수 생성하기

함수 선언문 방식은 앞서 설명한 함수 리터럴 형태와 같습니다.
여기서 주의할 점은 함수 선언문 방식으로 정의된 함수의 경우는반드시 함수명(함수이름)이 정의되어 있어야 한다는 것입니다.

다음의 코드는 함수 선언문 형태로 multiple() 함수를 구현한 코드로 함수명 multiple 이 있고, 또한 이 함수명으로 함수를 호출하고 있습니다.

함수 선언문
function multiple(x, y) {
    return x * y;
}

console.log(multiple(4,5)); // 20

정리하면, 함수 선언문은 아래의 그림과 같습니다.

 

 

#3-1 함수 표현식 방식으로 함수 생성하기

자바스크립트에서는 함수도 하나의 값처럼 취급됩니다.
이러한 값으로 취급되는 특징 때문에 자바스크립트 함수를 일급 객체라고 하며, 이 부분에 대해서는 추후에 다뤄보도록 하겠습니다.
그래서 값으로 취급되는 함수도 숫자나 문자열처럼 변수에 할당하는 것이 가능합니다.

그래서 함수 리터럴로 생성된 함수를 변수에 할당하여 함수를 생성하는 것을 함수 표현식(function expression)이라고 말합니다.

다음 예제는 add() 함수를 함수 표현식 형태로 생성한 것으로 함수 리터럴로 두 값을 더하는 함수를 생성한 다음, 이를 add 변수에 저장한 것입니다. 그리고 여기서 함수 리터럴로 생성한 함수는 함수명이 없으므로 익명함수(anonymous function) 입니다.

함수 표현식
// add() 함수생성(함수 표현식)
var add = function (x, y) {
    return x + y;
};

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

var plus = add;
console.log(plus(5, 6)); //11

앞 예제 코드에서 알 수 있듯이 함수 표현식은 함수 선언문 문법과 거의 유사합니다. 유일한 차이점은 함수 표현식 방법에서는 함수 이름이 선택 사항이며, 보통 사용하지 않습니다. 함수 표현식에서 함수 이름을 사용하는 경우는 아래에서 다시 살펴보도록 하겠습니다.

 

위 코드의 add 변수는 함수 리터럴로 생성한 함수를 참조하는 변수이지 함수 선언문에서 사용하는 함수 이름이 아니라는 것에 주의해야 합니다. 이 포스팅에서는 add 와 같이 함수가 할당된 변수를 함수 변수라고 부르도록 하겠습니다.

함수 변수 add 는 함수의 참조값을 가지므로 또 다른 변수 plus 에도 그 값을 그대로 할당할 수가 있습니다. 때문에 var plus = add; 에서 plus 함수 변수로 두 값을 더하는 코드가 제대로 수행될 수 있는 것입니다. 이는 앞서 학습했던 참조값의 특성을 잘 상기해 보시기 바랍니다.

그리고 위에서 봤다시피 함수 표현식으로 생성된 함수를 호출하려면 함수 변수를 사용해야 합니다.

위 코드의 함수 변수는 add 이므로, add(3, 4) 로 함수를 호출하는 것이 가능합니다.

plus 또한 add 함수 변수와 같은 함수를 참조하는 함수 변수이므로 plus(5, 6) 과 같은 형태로 함수를 호출하는 것이 가능한 것입니다.

앞에서 설명했듯이 이렇게 이름이 없는 함수 형태를 자바스크립트에서는 익명함수(anonymous function) 라고 부릅니다.

즉, 앞 예제 코드는 인자(argument)로 넘겨진 두 수를 더하는 익명함수를 만들고 이를 add 변수에 할당할 것입니다.

이것이 바로 익명 함수를 이용한 함수 표현식 방법(익명 함수 표현식)인 것입니다.

 

정리하면, 함수 표현식은 아래의 그림과 같습니다.

 


#3-2 기명 함수 표현식의 함수 호출

기명 함수 표현식은 함수 표현식이지만 이름이 있는 함수 표현식을 말합니다. 다시 말해, 함수 이름이 포함된 함수 표현식을 기명 함수 표현식이라고 부릅니다. 이러한 기명 함수 표현식을 사용할 경우에는 주의해야 할 점이 있습니다.

 

다음의 코드를 살펴보도록 하겠습니다.

기명 함수 표현식
var add = function sum(x, y) {
    return x + y;
};

console.log(add(3, 4)); // 7
console.log(sum(3, 4)); // sum is not defined 에러가 발생

위 코드를 분석해 보면 sum() 함수를 정의하고, 이 함수를 add 함수 변수에 할당했습니다. 예제이서 특이한 점은 add() 함수 호출 결과값은 성공적으로 반환되는 반면에, sum() 함수 호출의 경우 에러가 발생한다는 것입니다.

이것은 함수 표현식에서 사용된 함수 이름은 외부 코드에서 접근이 불가능하기 때문입니다.

실제로 함수 표현식에 사용된 sum 과 같은 함수 이름은 함수 내부(몸체)에서 해당 함수를 재귀적으로 호출하거나, 디버거 등에서 함수를 구분할 때 사용됩니다. 따라서 함수 이름으로 사용된 sum 으로 함수 외부에서 해당 함수를 호출할 때 sum() 함수가 정의되어 있지 않다는 에러가 발생하게 됩니다.

 

그렇다면 위에서 살펴본 함수 선언문 방식으로 정의한 함수는 어떻게 함수 이름으로 함수 외부에서 호출이 가능할까?

함수 선언문으로 작성한 코드는 아래와 같이 자바스크립트 엔진에 의해 다음과 같은 함수 표현식 형태로 변경되는 과정을 겪기 때문입니다.

js
// 함수 선언문 방식
function add(x, y) {
    return x + y;
}

// 자바스크립트 엔진에 의해 내부적으로 표현식 형태로 변경됨
var add = function add(x, y) {
    return x + y;
}

함수 이름과 함수 변수의 이름이 add 로 같으므로, 함수 이름으로 함수가 호출되는 것처럼 보이지만, 실제로는 add 함수 변수로 함수 외부에서 호출이 가능하게 된 것입니다.

이미 언급했듯이 함수 표현식에서는 함수 이름이 선택 사항이지만, 이러한 함수 이름을 이용하면 함수 코드 내부에서 함수 이름으로 함수의 재귀적인 호출 처리가 가능해집니다.

 

다음 예제는 기명 함수 표현식으로 함수 이름으로 함수의 재귀적 호출을 하는 코드입니다.

js
var factorilVar = function factorial(n) {
    if(n <=1 ) {
        return 1;
    }
    // debugger;
    return n * factorial(n-1);
};

console.log( factorilVar( 3 ) ); // 6
// console.log( factorial( 3 ) ); // factorial is not defined 에러 발생

위처럼 함수 표현식 방식으로 팩토리얼값을 재귀적으로 호출,구현한 함수입니다.

함수 외부에서는 함수 변수 factorilVar 로 함수를 호출했으며, 함수 내부에서 이뤄지는 재귀 호출은 factorial 함수 이름으로 처리하는 것을 확인하실 수 있습니다.   앞서 설명한 것과 마찬가지로 함수 외부에서는 함수명 factorial() 으로 해당 함수를 호출하지 못해 에러가 발생한 것입니다.

 


#4 함수 선언문과 함수 표현식 비교

위에서 설명드린 내용은 아래 그림과 같습니다.

 

 

Conclusion

지금까지 자바스크립트에서 함수를 생성하는 3가지 방법에 대해 알아보았습니다. 코드는 약간씩 다르지만 모두 같은 기능의 함수를 생성함을 확인할 수 있었습니다.   하지만 이들 사이에는 동작 방식이 약간 차이가 있습니다 그 이유 중의 하나가 바로 호이스팅(Hoisting) 때문입니다.

다음 포스팅에서는 호이스팅에 관련된 내용들을 살펴보도록 하겠습니다.

 

 

Jaehee's WebClub

 

 

'Study Group > javaScript 기초' 카테고리의 다른 글

JS Basics #2 - Function  (0) 2019.09.04

댓글을 달아 주세요