개발/Javascript

4. Javascript의 함수

JonghwanWon 2021. 7. 15. 15:51

함수란 어떤 작업을 수행하기 위한 로직을 정의 한 코드블록 입니다.

일반적으로 반복 되는 작업을 함수로 정의하고 재사용하여 효율적인 프로그래밍을 가능하게 합니다.

이외에 객체의 메소드, 정보 은닉, 클로저 등의 기능을 수행 할 수 있습니다.

 

1. 함수 정의

자바스크립트에서 함수를 정의하는 방법은 3가지가 있습니다.

  1. 함수 선언문
  2. 함수 표현식
  3. Function 생성자 함수

1-1 함수 선언문

함수 선언문은 일반적으로 함수를 정의 할 때 사용되는 방법입니다.

function 키워드와 함께 함수명, 매개변수(parameter), 함수 몸체로 이루어져 있습니다.

// 함수 선언문
//
// 함수명은 해당 함수를 구분할 수 있는 식별자(Identifier)로
// 함수 선언문에서 함수명은 반드시 필요합니다.
function sum(num1, num2) {
	return num1 + num2; // 반환 값
}

1-2 함수 표현식

함수 표현식에 알아보기 앞서 일급객체에 대해 알아보겠습니다.

 

일급객체란, 다음의 세가지 조건을 만족하는 경우를 의미합니다.

  1. 변수에 할당 할 수 있다.
  2. 함수의 매개변수로 전달 받을 수 있다.
  3. 함수의 결과로써 반환 될 수 있다.

자바스크립트의 함수는 위의 조건을 모두 만족하므로 일급객체입니다.

일급객체의 특징을 이용해 함수를 정의하고, 변수에 할당 할 수 있는데 이것을 함수 표현식 이라 합니다.

// 함수 표현식

// 익명함수 표현식
var sum = function(a, b) {
	return a + b;
}

// 기명함수 표현식
var foo = function multiply(a, b) {
	return a * b;
}

console.log(sum(1, 1)); // 2
console.log(multiply(5, 2)); // Uncaught ReferenceError: multiply is not defined

함수 선언문과 달리 함수 표현식으로 정의한 함수는 함수명을 생략할 수 있습니다.

이를 익명함수(anonymous function)이라고 부릅니다.

함수 표현식에서 함수명은 생략하는것이 일반적입니다.

하지만 때때로 이름을 지정하는 경우가 생기는데요 이런 경우 기명함수(named function)라고 부릅니다.

 

그렇다면 기명함수가 되면 어떤 차이가 있을까요?

  1. 이름을 사용해 표현식 내부에서 자기 자신을 참조 할 수 있습니다.
  2. 기명 함수 표현식 외부에선 그 이름을 사용할 수 없습니다.
function foo = function sayHello(name) {
	if(name) {
    	alert(`Hello, ${name}`);
    } else {
    	sayHello('Unknown');
    }
}

console.log(foo('World')); // 'Hello, World'
console.log(foo()); // 'Hello, Unknown'

 

기명함수 표현식을 사용 시 할당 된 변수를 호출하는게 아닌, 기명함수의 함수명을 사용해 호출하기 되면 에러가 발생합니다.

이는 함수표현식에서 사용 된 함수명은 외부에서 접근할 수 없기 때문입니다. (함수 선언식의 경우도 마찬가지 입니다)

function multiply(a, b) {
	return a * b;
}

// 함수 선언문으로 작성 된 함수를 Javascript 엔진에서는 아래와 같이
// 함수 표현식으로 형태가 변경되어 읽게 됩니다.
var multiply = function multiply(a, b) {
	return a * b;
}

결국 함수 선언문도 함수 표현식과 동일하게 정의 되는 것을 알 수 있습니다.

 

1-3 Function 생성자 함수

이전에 알아본 것 처럼 함수 선언문이나 함수 표현식 모두 변수에 함수객체를 담아 정의 되는데,

생성자 함수를 통한 함수의 정의는 이를 단순화 시킨 short-hand(축약법)입니다.

 

Function 생성자 함수로 함수를 생성하는 방법은 다음과 같습니다

var multiply = new Function('a', 'b', 'return a * b');
console.log(multiply(5, 2)); // 10

생성자 함수를 통한 함수의 정의는 일반적으로 사용하지 않습니다.

 

2. 함수의 호이스팅(hoisting)

 

JavaScript에서 호이스팅(hoisting)이란, 인터프리터가 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것을 의미합니다.

주로 호이스팅은 선언부를 함수 범위의 최상단으로 끌어올리는 현상이라 말하곤 합니다.

자바스크립트는 모든 선언문(var, let, const, function, class)을 선언되기 이전에 참조할 수 있습니다.

 

var로 선언한 변수의 경우 호이스팅 시 undefined로 변수를 초기화합니다.

letconst로 선언한 변수의 경우 호이스팅 시 변수를 초기화하지 않습니다.

 

단, let으로 선언된 변수는 var로 선언된 변수와 달리 할당문을 만나기 전까지 참조할 수 없는 구간을 일시적 사각지대(Temporal Dead Zone; TDZ)라고 하는데,

일시적 사각지대에 있는 변수를 참조하려 한다면 ReferenceError가 발생합니다

초기화를 수행하기 전에 읽는 코드가 먼저 나타나면 예외가 발생할 수 있음을 유의해야 합니다.

 

자바스크립트의 변수는 선언-초기화-할당의 과정을 거쳐 실제 사용할 수 있는 값으로 참조 할 수 있게 됩니다.

var 키워드로 선언된 변수의 생명 주기

변수의 선언과 호이스팅이 실제로는 어떻게 동작하는지 코드로 보겠습니다.

// 작성된 코드
console.log(foo); // undefined
var foo = 'abc';   // 변수 foo의 선언과 동시에 초기화
console.log(foo); // 'abc'
foo = 123;        // 변수 foo의 값을 재할당
console.log(foo); // 123

// 호이스팅 예시
var foo;          // 변수의 선언부를 최상단으로 끌어올림

console.log(foo); // 할당 된 값이 없으므로 undefined 출력
foo = 'abc';      // 변수 foo의 값을 할당
console.log(foo); // 'abc'
foo = 123;        // 변수 foo의 값을 할당
console.log(foo); // 123

예시와 같이 변수의 선언부가 최상단으로 끌어올려 선언하기도 전에 사용 된 변수에 접근이 가능했습니다.

이는 var 키워드로 생성 된 변수는 블록 레벨 스코프(block-level scope)가 아닌 함수 레벨 스코프(function-level scope)를 갖기 때문입니다.

다른 글에서 변수와 스코프에 대해 상세히 알아보도록 하겠습니다.

 

자, 그럼 함수의 호이스팅은 어떨까요? 

함수는 정의 방식에 따라 동작하는 방법이 약간씩 차이가 있습니다.

 

결과부터 말하자면, 함수 선언문으로 정의 된 함수는 호이스팅되어 선언 이전에 사용이 가능하고

함수 표현식으로 정의 된 함수는 변수의 호이스팅이 발생하여 선언 이전에 사용이 불가합니다.

 

어떤 말인지 코드로 살펴보겠습니다.

// 실제 코드
console.log(multiply(5, 2)); // 10

function multiply(a, b) {
	return a * b;
}

// 호이스팅 예시
function multiply(a, b) {
	return a * b;
}

console.log(multiply(5, 2)); // 10

함수 선언문으로 작성 된 코드는 선언과 초기화 할당이 한번에 이루어지기 때문에 호이스팅되어 사용이 가능합니다.

이어서, 함수 표현식으로 작성 된 코드와는 어떤 차이인지 알아보겠습니다.

// 실제 코드
console.log(multiply(5, 2)); // TypeError: multiply is not a function

var multiply = function(a, b) {
	return a * b;
}

// 호이스팅 예제
var multiply; 				 // undefined

console.log(multiply(5, 2)); // TypeError: multiply is not a function

multiply = function(a, b) {
	return a * b;
}

함수 선언문과는 달리 선언부만 최상단으로 끌어올려 undefined 상태일때 호출하려고 하니 TypeError가 발생하게 됩니다.

 

3. 매개변수 (parameter)

함수를 정의 할 때 원하는대로 동작하기 위해 추가적인 정보가 필요할 수 있습니다.

이 때에 매개변수를 지정하는데요, 매개변수는 함수 내에서 변수와 동일하게 동작합니다.

함수에 전달 한 인수(argument)는 매개변수에 할당되며, 인수를 전달하지 않은 경우 undefined로 초기화 됩니다.

var myFunction = function(a, b) {
	console.log(a, b);
}

myFunction(1); // 1, undefined

Call-by-value

원시타입 형태의 인수는 Call-by-value(값에 의한 호출)로 동작합니다.

이는 전달 된 인수는 매개변수에 값을 복사하여 함수로 전달하게 되는 방식입니다.

즉, 함수 내에서 매개변수의 값이 변경되어도 인수로 전달 된 값은 변하지 않습니다.

var x = 0;

function increase(number) {
	number += 1;
	return number;
}

console.log(increase(x)); // 1
console.log(x);           // 0

 

그렇다면, 참조형 데이터 타입은 어떨까요?

인수로 전달된 객체도 마찬가지로 Call-by-value로 동작합니다.

단, 참조형 데이터타입은 값이 담긴 주솟값들로 이루어진 묶음을 가리키는 주솟값을 복제해 함수로 전달합니다.

 

그렇기 때문에 property의 값 변경은 이루어지지만, 객체 그 자체는 변경되지 않습니다.

var obj = {
  name: "John Doe",
};
var obj2 = {
  name: "Jane Roe",
};

function changeName(obj, name) {
  obj.name = name;
}

function changeObj(obj) {
  obj = { name: "Jain Smith" };
}

changeName(obj, "Nick");
changeObj(obj2);

console.log(obj); // { name: 'Nick' }
console.log(obj2); // { name: 'Jane Roe' }

이처럼 어떤 외부 상태를 변경하는 부수효과(side effect)를 발생시키는 함수를 비순수 함수라 하고, 그렇지 않은 반대되는 경우를 순수함수라 합니다.

비순수함수는 부수효과를 발생시키기 때문에 복잡성이 증가 합니다.

따라서 비순수 함수를 최대한 줄이는 것은 디버깅을 쉽게 만들 수 있습니다.

4. 반환값

함수는 호출되었을 때 내부 로직을 수행한 후 결과를 반환(return)할 수 있습니다.

이때 반환 된 값을 반환값(return value)라고 합니다.

  1. 함수는 단일값 뿐만 아니라 배열, 객체 등을 이용해 값을 리턴할 수 있습니다.
  2. 함수는 return을 생략 할 수 있습니다. 이때 암묵적으로 undefined를 반환하게 됩니다.
  3. 자바스크립트 해석기는 return 키워드를 만나면 해당 함수의 실행을 중단하고 호출되었던 곳으로 되돌아갑니다.
      즉, return 키워드 위후 작성 된 다른 구문들은 실행되지 않는 것을 의미합니다.

 

// 단일 값의 반환
function multiply(a, b) {
	var result = a * b;
    
	return result;
    
    console.log('이 구문은 return 키워드 이후 존재하기에 실행되지 않습니다!')
}

// 객체의 반환
function calc(a, b) {
	var sum = a + b;
    var average = sum / 2;
    
    return { sum, average };
}

'개발 > Javascript' 카테고리의 다른 글

6. Javascript의 strict mode  (0) 2021.07.19
5. Javascript의 스코프  (0) 2021.07.15
3. Javascript의 타입 변환  (0) 2021.07.14
2. Javascript의 데이터 타입  (0) 2021.07.14
1. Javascript와 브라우저  (0) 2021.07.14