함수란?

컴퓨터 프로그래밍 언어에서 함수(function)는 일련의 과정을 문(statement)으로 구현한 코드 블록으로 감싸서 하나의 실행 단위로 정의한 것이다.

즉, 함수는 '어떤 역할을 수행하는 것' 쯤으로 생각할 수 있다. 이러한 함수를 사용하는 가장 큰 이유는 재사용이 가능하기 때문!

이러한 재사용성은 유지보수의 편의성을 높이고 실수를 줄여 코드의 신뢰성을 높인다.

그렇다면 언제든 필요할 때마다 함수를 호출하여 사용하기 위해선 어떻게 해야 할까? 🧐

1. 함수 정의와 호출

함수 정의 (function definition)

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

자바스크립트의 함수는 다양한 방법으로 정의 가능한데, 위 코드는 그 중 함수 선언문을 통해 함수를 정의한 예다.

함수 정의만으로 함수가 실행되는 것은 아니기에, 함수 호출이 필요한데...
이는 어떻게 할까?

함수 호출 (function call/invoke)

var result = add(2, 5); // 7

일련의 과정을 실행하기 위해 필요한 입력, 즉 인수(argument)를 매개변수를 통해 함수에 전달하면서 함수의 실행을 명시적으로 지시하는 것이 함수 호출이다!

함수를 호출하면 코드 블록에 담긴 문들이 일괄적으로 실행되고, 실행 결과, 즉 반환값을 반환한다.

2. 매개변수

매개변수(parameter)

이때, 함수를 실행하기 위해 필요한 값을 함수 외부에서 내부로 전달할 필요가 있는 경우, 매개변수(parameter)를 통해 인수를 전달한다.

매개변수는 함수를 정의할 때 선언하며, 함수 몸체 내부에서 변수와 동일하게 취급된다.

함수가 호출되면 암묵적으로 매개변수가 생성되고, 일반 변수와 마찬가지로 undefined 타입으로 초기화된 이후 인수가 순서대로 할당된다!

또한, 자바스크립트 함수에서는 인수와 매개변수의 개수가 일치하지 않아도 오류가 발생하지 않는다. 그러나 함수 호출에서 전달되는 인수의 개수함수 정의에서 사용되는 매개변수의 개수가 다른 경우에는 매개변수의 기본 값(default value)을 설정하여 사용하여야 한다.

function add(x, y=5) { // 매개변수 y의 기본값을 5로 설정
     return x + y;
}

var result = add(2); // 7

자바스크립트는 함수 정의 시 매개변수의 개수를 미리 정하지 않고 매개변수의 수를 무한대로 설정하는 것 또한 가능한데, 이때 사용되는 매개변수가 나머지 매개변수(rest parameter)다.

function sum(...scores) { // 매개변수 y의 기본값을 5로 설정
  let sum = 0;
  for (let score of scores) {
    sum += score;
  }
  
  return sum;
}

위 코드에서 매개변수 scores는 함수 호출 시 전달받은 값을 배열 형태로 가지고 연산을 진행한다.



익명 함수

자바스크립트의 함수 중, 함수 이름이 없는 함수가 존재한다.

바로 익명함수(anonymous function)인데, 익명 함수는 변수를 선언하는 것과 같기 때문에 (함수 리터럴) 익명 함수에 의해 생성되는 함수의 이름을 쉽게 변경할 수 있다.

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

1. 자기호출 익명 함수

(function () {
  var x = 2;
  var y = 5;
  return x + y; // 7
}());

함수 정의와 동시에 즉시 호출되는 함수를 즉시 실행 함수(IIFE)라고 한다.

즉시 실행 함수는 단 한 번만 호출되어 다시 호출할 수 없으며, 익명 함수를 사용하는 것이 일반적이다. 함수 이름이 있는 기명 함수를 사용할 수는 있으나 다시 호출할 수 없다.

2. 화살표 함수

const add = (x, y) => x + y;

화살표 함수(arrow function)은 익명 함수를 축약해서 표현하는 형태다.

화살표 함수에서는 익명 함수에서 사용되는 키워드 function, return, 중괄호({}) 대신 화살표 기호(=>)를 사용한다!

3. 호이스팅

자바스크립트는 기본적으로 변수나 함수 정의부를 프로그램 제일 앞으로 끌어 올리는 호이스팅(hoisting)이란 특징이 있다.

C, 자바, 파이썬 등의 프로그램에서는 함수 호출이 함수 정의보다 먼저 일어날 경우 오류가 발생하지만 자바스크립트는 호이스팅 기능으로 인하여 오류가 나지 않는다.

그러나, 익명 함수의 경우에는 일반적인 함수와는 달리 함수를 정의하여 변수를 저장해서 사용하는 형태이기 때문에 반드시 함수 호출 이전에 익명 함수를 선언하여 사용하여야 한다.



자바스크립트 스코프

스코프(scope)는 '범위'란 뜻을 가진 용어로, 자바스크립트 뿐만 아니라 다른 프로그래밍 언어에서도 사용되는 개념이다.

스코프는 단어가 가진 의미대로 변수에 접근할 수 있는 유효 범위를 의미하며, 아주 중요한 개념이다!

var x = "global x"; // 전역 변수
var y = "global y";

function outer() {
  var z = "outer's local z"; // 지역 변수
  
  console.log(x); // global x
  console.log(y); // global y
  console.log(z); // outer's local z
  
  function inner() {
    var x = "inner's local x"; // 지역 변수
    
    console.log(x); // inner's local x
  	console.log(y); // global y
    console.log(z); // outer's local z
  }
  
  inner();
}

outer();

console.log(x); // global x
console.log(z); // ReferenceError: z is not defined
  

전역에 변수를 선언하면 전역 스코프를 갖는 전역 변수(global variable)가 된다.

지역에 변수를 선언하면 지역 스코프를 갖는 지역 변수(local variable)가 된다.

지역이란 함수 몸체 내부를 의미하며, 지역 변수는 자신이 선언된 지역과 하위 지역(중첩 함수)에서만 참조 가능하다.

다시 말해, 지역 변수는 자신의 지역 스코프와 하위 지역 스코프에서만 유효하다.



클로저

위에서 전역 변수에 대해 이야기했는데, 전역 변수는 편리한 점도 있지만...

다른 측면에서 보면 전역 변수는 어디서나 값이 변경될 수 있기 때문에 보안에 취약하고 예기치 못한 오류를 발생시킬 가능성을 가지고 있다. 🤯

클로저(closure)는 전역 변수가 아닌 변수를 전역 변수와 같은 방식으로 동작하는 동시에, 함부로 그 값이 변경될 수 없도록 변수를 '사유화'하는 방법을 제공함으로써 이를 해결한다!

물론 클로저는 난해하기로 유명한 자바스크립트의 개념 중 하나다. 함수를 일급 객체로 취급하는 함수형 프로그래밍 언어에서 사용되는 중요한 특성이며, MDN에서는 클로저에 대해 다음과 같이 정의하고 있다.

클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다.

이게 대체... 무슨 소리일까?

렉시컬 스코프

우선 렉시컬 스코프(정적 스코프) 가 가진 의미를 알아보자. 자바스크립트 엔진은 함수를 어디서 호출했는지가 아니라 함수를 어디에 정의했는지에 따라 상위 스코프를 결정하는데, 이를 렉시컬 스코프라 한다.

const x = 1;

function outerFunc() {
  const x = 10;

  function innerFunc() {
    console.log(x); // 10
  }
  
  innerFunc();
}

outerFunc();
  

위 코드에서 outerFunc 함수 내부에서 중첩 함수 innerFunc가 정의되고 호출되었다. 이때 innerFunc의 상위 스코프는 외부 함수 outerFunc의 스코프다. 따라서 중첩 함수 innerFunc 내부에서 자신을 포함하고 있는 외부 함수 outerFunc의 x 변수에 접근할 수 있다.

그런데, innerFunc 함수가 outerFunc 함수의 내부에서 정의된 중첩 함수가 아니라면 어떻게 될까?

그렇다. innerFunc 함수를 outerFunc 함수의 내부에서 호출한다 하더라도 outerFunc 함수의 변수에 접근할 수 없다!

const x = 1;

function outerFunc() {
  const x = 10;  
  innerFunc();
}

function innerFunc() {
  console.log(x); // 1
}

outerFunc();
  

이와 같은 현상이 발생하는 이유는 자바스크립트가 렉시컬 스코프를 따르는 언어이기 때문이다.

클로저

이러한 렉시컬 환경을 활용하는 클로저의 예제를 살펴보자.

const x = 1;

// 1️⃣
function outer() {
  const x = 10;  
  const inner = function () { console.log(x); }; //2️⃣
  return inner;
}

function innerFunc = outer(); // 3️⃣

innerFunc(); // 4️⃣ 10
  

outer 함수를 호출(3️⃣)하면 outer 함수는 중첩 함수 inner를 반환하고 생명 주기(Life cycle)을 마감한다.

이때 outer 함수의 지역 변수 x와 변수 값 10을 저장하고 있던 outer 함수가 실행 컨텍스트에서 제거되었으므로 outer 함수의 지역 변수 x 또한 생명 주기를 마감한다.

따라서 outer 함수의 지역 변수 x는 접근할 방법이 달리 없어 보인다.

그러나 위 코드의 실행 결과(4️⃣)는 outer 함수의 지역 변수 x의 값인 10이다.

이처럼 외부 함수보다 중첩 함수가 더 오래 유지되는 경우 중첩 함수는 이미 생명 주기가 종료한 외부 함수의 변수를 참조할 수 있다. 이러한 중첩 함수를 클로저(closure)라 한다!



지금까지 자바스크립트의 함수에 대해 알아보았다.

다음 글에서는 객체에 대해 알아보도록 하자! 😝

profile
이게.., 김민경이,.., 그려준 나.,.,.?!

0개의 댓글