본문 바로가기

Development/Javascript

모던 자바스크립트 입문 - Chapter 8: 함수 💻

안녕하세요! 오늘은 자바스크립트의 핵심 개념 중 하나인 함수에 대해 완전히 정복해보겠습니다.

함수 부분은 공부해야 할 내용들이 많이 있어 긴글 인점 양해 바랍니다!

모던 자바스크립트 입문 외에 다른곳에서도 참고해서 작성된 내용이 있습니다.


📌 목차

  • 8.1 함수의 정의하기
  • 8.2 함수 호출하기
  • 8.3 함수의 인수
  • 8.4 재귀 함수
  • 8.5 프로그램의 평가와 실행 과정
  • 8.6 클로저
  • 8.7 이름 공간 (생략)
  • 8.8 객체로서의 함수
  • 8.9 고차 함수
  • 8.10 콜백함수
  • 8.11 ECMAScript 6부터 추가된 함수의 기능

📝 함수의 정의하기

함수를 정의하는 방법은 네 가지입니다.

1️⃣ 함수 선언문(Function Declaration)

function square(x) { 
    return x * x; 
}

2️⃣ 함수 표현식(Function Expression)

// 익명 함수 표현식
let square = function(x) { 
    return x * x; 
};

// 기명 함수 표현식
let factorial = function fact(n) {
    return n <= 1 ? 1 : n * fact(n - 1);
};

3️⃣ Function 생성자로 정의하는 방법

let square = new Function("x", "return x * x");

// 여러 매개변수가 있는 경우
let add = new Function("a", "b", "return a + b");

⚠️ 주의: Function 생성자는 성능상 권장되지 않으며, 보안 이슈가 있을 수 있습니다.

4️⃣ 화살표 함수 표현식(Arrow Function) - ES6+

// 기본 형태
let square = x => x * x;

// 매개변수가 여러 개인 경우
let add = (a, b) => a + b;

// 함수 본문이 여러 줄인 경우
let multiply = (a, b) => {
    console.log(`${a} × ${b} 계산 중...`);
    return a * b;
};

// 객체를 반환하는 경우 (괄호 필요)
let createPerson = (name, age) => ({ name, age });

📝 함수 호출하기

1️⃣ 함수 호출

함수의 참조가 저장된 변수 뒤에 호출 연산자인 ()를 붙여서 함수를 호출합니다.

function greet(name) {
    return `안녕하세요, ${name}님!`;
}

let message = greet("홍길동");
console.log(message); // "안녕하세요, 홍길동님!"

2️⃣ 메서드 호출

객체의 프로퍼티에 저장된 값이 함수 타입일 때는 그 프로퍼티를 메서드라고 부릅니다.

let person = {
    name: "김철수",
    greet: function() {
        return `안녕하세요, 저는 ${this.name}입니다.`;
    },
    // ES6 축약 문법
    introduce() {
        return `제 이름은 ${this.name}입니다.`;
    }
};

console.log(person.greet()); // "안녕하세요, 저는 김철수입니다."
console.log(person.introduce()); // "제 이름은 김철수입니다."

3️⃣ 생성자 호출

함수 또는 메서드를 호출할 때 함수의 참조를 저장한 변수 앞에 new 키워드를 추가하면 함수가 생성자로 동작합니다.

function Person(name, age) {
    this.name = name;
    this.age = age;
    this.introduce = function() {
        return `저는 ${this.name}이고, ${this.age}살입니다.`;
    };
}

let person1 = new Person("이영희", 25);
console.log(person1.introduce()); // "저는 이영희이고, 25살입니다."

4️⃣ call, apply, bind를 사용한 간접 호출

함수의 call, apply, bind 메서드를 사용하면 함수를 간접적으로 호출할 수 있습니다.

function introduce(greeting, punctuation) {
    return `${greeting}, 저는 ${this.name}입니다${punctuation}`;
}

let person = { name: "박민수" };

// call: 인수를 개별적으로 전달
console.log(introduce.call(person, "안녕하세요", "!")); 
// "안녕하세요, 저는 박민수입니다!"

// apply: 인수를 배열로 전달
console.log(introduce.apply(person, ["반갑습니다", "."])); 
// "반갑습니다, 저는 박민수입니다."

// bind: 새로운 함수를 생성 (나중에 호출)
let boundIntroduce = introduce.bind(person, "어서오세요");
console.log(boundIntroduce("~")); // "어서오세요, 저는 박민수입니다~"

📝 함수의 인수

인수의 생략

함수 정의에서 선언된 매개변수 개수보다 인수를 적게 전달하면, 생략된 매개변수는 undefined가 됩니다.

function greet(firstName, lastName) {
    console.log(`안녕하세요, ${firstName} ${lastName}님!`);
}

greet("홍"); // "안녕하세요, 홍 undefined님!"

기본값 매개변수 (ES6+)

매개변수에 기본값을 설정할 수 있습니다.

function greet(firstName, lastName = "님") {
    console.log(`안녕하세요, ${firstName} ${lastName}!`);
}

greet("홍길동"); // "안녕하세요, 홍길동 님!"
greet("김철수", "씨"); // "안녕하세요, 김철수 씨!"

가변 길이 인수 목록

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

console.log(sum(1, 2, 3, 4, 5)); // 15

// ES6+ 나머지 매개변수 사용
function sumES6(...numbers) {
    return numbers.reduce((total, num) => total + num, 0);
}

console.log(sumES6(1, 2, 3, 4, 5)); // 15

📝 재귀 함수

재귀 함수는 자기 자신을 호출하는 함수입니다.

재귀 함수의 기본

function fact(n) {
	if(n <= 1) return 1;
    return n*fact(n-1);
}

fact(5); // 120

 


📝 프로그램의 평가와 실행 과정

실행 컨텍스트(Execution Context)

자바스크립트 엔진은 실행 가능한 코드를 만나면 그 코드를 평가해서 실행 문맥으로 만듭니다.

실행 가능한 코드의 유형은:

  • 전역 코드
  • 함수 코드
  • eval 코드

자바스크립트 엔진이 실행 가능한 코드의 유형을 분류하는 이유는 실행 문맥을 초기화하는 환경과 과정이 다르기 때문이다

this값

함수가 호출되어 실행되는 시점에 this 값이 결정됩니다. 

이 this 값은 '함수가 호출되었을 때 그 함수가 속해 있던 객체의 참조'이며 실행 문맥의 디스 바인딩 컴포넌트가 참조하는 객체입니다.

1️⃣ 최상위 레벨 코드의 this - 전역 객체 (window)

console.log(this); // Window 객체 (브라우저 환경)
console.log(this === window); // true

// 전역 변수는 window의 속성이 됨
var globalVar = "전역변수";
console.log(this.globalVar); // "전역변수"

 

2️⃣ 이벤트 처리기 안에 있는 this - 이벤트 대상 요소

const button = document.querySelector('button');

button.addEventListener('click', function() {
    console.log(this); // <button> 요소
    console.log(this.textContent); // 버튼의 텍스트 내용
    this.style.color = 'red'; // 클릭한 버튼의 글자색 변경
});

// 화살표 함수는 상위 스코프의 this를 사용
button.addEventListener('click', () => {
    console.log(this); // Window 객체 (상위 스코프)
});

 

3️⃣ 생성자 함수 안에 있는 this - 생성된 인스턴스 객체

function Car(brand, model) {
    this.brand = brand;     // this = 새로 생성될 객체
    this.model = model;
    this.start = function() {
        console.log(`${this.brand} ${this.model} 시동 켜짐!`);
    };
}

const myCar = new Car('현대', '아반떼');
console.log(myCar.brand); // "현대"
myCar.start(); // "현대 아반떼 시동 켜짐!"

 

4️⃣ 생성자의 prototype 메서드 안에 있는 this - 생성된 인스턴스 객체

function Person(name) {
    this.name = name;
}

Person.prototype.introduce = function() {
    console.log(`안녕하세요, ${this.name}입니다!`);
    // this = 메서드를 호출한 인스턴스 객체
};

const person1 = new Person('김개발');
const person2 = new Person('이자바');

person1.introduce(); // "안녕하세요, 김개발입니다!"
person2.introduce(); // "안녕하세요, 이자바입니다!"

 

5️⃣ 직접 호출한 함수 안에 있는 this - 전역 객체 (strict mode에서는 undefined)

function normalFunction() {
    console.log(this); // Window 객체 (strict mode에서는 undefined)
}

const obj = {
    method: function() {
        console.log(this); // obj 객체
        
        function innerFunction() {
            console.log(this); // Window 객체 (내부 함수는 전역)
        }
        innerFunction();
    }
};

normalFunction(); // 직접 호출
obj.method(); // 객체의 메서드로 호출

 

6️⃣ apply와 call , bind 메서드로 호출한 함수 안에 this - 첫 번째 인수로 전달한 객체

function greet(greeting, punctuation) {
    console.log(`${greeting}, ${this.name}${punctuation}`);
}

const person = { name: '박자바' };
const company = { name: '테크기업' };

// call: 인수를 개별적으로 전달
greet.call(person, '안녕하세요', '!'); // "안녕하세요, 박자바!"
greet.call(company, '환영합니다', '.'); // "환영합니다, 테크기업."

// apply: 인수를 배열로 전달
greet.apply(person, ['반갑습니다', '~']); // "반갑습니다, 박자바~"

// bind: this가 고정된 새 함수 생성
const boundGreet = greet.bind(person);
boundGreet('좋은 하루', '♪'); // "좋은 하루, 박자바♪"

📝 클로저

클로저를 프로그래밍 언어적인 관점에서 설명하면 같은 동작을 하는 함수와 그 기능을 구현한 자료 구조의 모음이라고 할 수 있습니다

자기 자신이 정의된 환경에서 함수 안에 있는 자유 변수의 식별자 결정을 실행한다.

기본 클로저

function outerFunction(x) {
    // 외부 함수의 변수
    
    function innerFunction(y) {
        // 내부 함수에서 외부 함수의 변수에 접근
        return x + y;
    }
    
    return innerFunction; // 내부 함수를 반환
}

let addFive = outerFunction(5);
console.log(addFive(3)); // 8 (5 + 3)

 

 


📝 객체로서의 함수

자바스크립트에서 함수는 일급 객체(First-class Object)입니다.

함수의 프로퍼티와 메서드

function myFunction(a, b) {
    return a + b;
}

// 함수 자체의 프로퍼티
console.log(myFunction.name);       // "myFunction"
console.log(myFunction.length);     // 2 (매개변수 개수)

// 함수에 사용자 정의 프로퍼티 추가
myFunction.description = "두 수를 더하는 함수";
myFunction.version = "1.0";

console.log(myFunction.description); // "두 수를 더하는 함수"

 

함수의 프로퍼티

프로퍼티 이름 설명
caller 현재 실행 중인 함수를 호출한 함수
length 함수의 인자 개수
name 함수를 표시할 때 사용하는 이름
prototype 프로토타입 객체의 참조

Function.prototype의 프로퍼티

프로퍼티 이름 설명
apply() 선택한 this와 인수를 사용하여 함수를 호출한다. 인수는 배열 객체이다.
bind() 선택한 this와 인수를 적용한 새로운 함수를 반환한다
call() 선택한 this와 인수를 사용하여 함수를 호출한다. 인수는 쉼표로 구분한 값이다.
constructor Function 생성자의 참조
toString() 함수의 소스 코드를 문자열로 만들어 반환한다

📝고차 함수

고차 함수란? 함수를 다루는 함수입니다!

  • 함수를 받아서 사용하거나
  • 함수를 만들어서 돌려주는 함수

1️⃣ 함수를 만들어 주는 함수

// 곱하기 함수를 만드는 공장
function makeMultiplier(num) {
    return function(x) {
        return x * num;
    };
}

// 2배, 3배 함수 만들기
const double = makeMultiplier(2);
const triple = makeMultiplier(3);

console.log(double(5)); // 10 (5 × 2)
console.log(triple(4)); // 12 (4 × 3)

2️⃣ 함수를 받아서 사용하는 함수

// 계산기 함수
function calculator(a, b, operation) {
    return operation(a, b);
}

// 더하기, 빼기 함수
const add = (x, y) => x + y;
const subtract = (x, y) => x - y;

console.log(calculator(10, 5, add));      // 15
console.log(calculator(10, 5, subtract)); // 5

3️⃣ 배열에서 자주 쓰는 고차 함수들

const numbers = [1, 2, 3, 4, 5];

// map: 모든 요소를 바꾸기
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]

// filter: 조건에 맞는 것만 골라내기
const evens = numbers.filter(num => num % 2 === 0);
console.log(evens); // [2, 4]

// 한 번에 여러 작업하기
const result = numbers
    .filter(num => num > 2)  // 2보다 큰 수만
    .map(num => num * 10);   // 10배로 만들기

console.log(result); // [30, 40, 50]

4️⃣ 실용적인 예시

// 인사말 만들기
function createGreeting(greeting) {
    return function(name) {
        return `${greeting}, ${name}님!`;
    };
}

const sayHello = createGreeting('안녕하세요');
const sayGoodbye = createGreeting('안녕히 가세요');

console.log(sayHello('김철수'));    // "안녕하세요, 김철수님!"
console.log(sayGoodbye('이영희'));  // "안녕히 가세요, 이영희님!"

 

💡 왜 고차 함수를 쓸까요?

코드 재사용: 비슷한 함수를 여러 번 만들 필요 없음
깔끔한 코드: 복잡한 로직을 간단하게 표현
유연함: 상황에 맞는 함수를 동적으로 생성

핵심: 고차 함수는 함수를 재료로 사용해서 새로운 함수를 만들거나, 함수에게 을 시키는 함수입니다!


📝 콜백함수

콜백 함수는 다른 함수에 인수로 전달되어 나중에 호출되는 함수입니다.

기본 콜백 함수

function processUserInput(callback) {
    let name = "홍길동";
    let age = 25;
    
    // 콜백 함수 호출
    callback(name, age);
}

function greetUser(name, age) {
    console.log(`안녕하세요, ${name}님! ${age}세시군요.`);
}

function showUserInfo(name, age) {
    console.log(`사용자 정보: 이름=${name}, 나이=${age}`);
}

// 다른 콜백 함수 사용
processUserInput(greetUser);    // "안녕하세요, 홍길동님! 25세시군요."
processUserInput(showUserInfo); // "사용자 정보: 이름=홍길동, 나이=25"

📝 ES6+ 함수의 새로운 기능들

1️⃣ 기본 매개변수 (Default Parameters)

// 옛날 방식
function greet(name, msg) {
    msg = msg || "안녕하세요";
    return msg + ", " + name;
}

// 새로운 방식
function greet(name, msg = "안녕하세요") {
    return `${msg}, ${name}`;
}

console.log(greet("홍길동")); // "안녕하세요, 홍길동"
console.log(greet("김철수", "반갑습니다")); // "반갑습니다, 김철수"

2️⃣ 나머지 매개변수 (...rest)

// 여러 개의 인수를 배열로 받기
function sum(...numbers) {
    return numbers.reduce((total, num) => total + num, 0);
}

console.log(sum(1, 2, 3, 4, 5)); // 15
console.log(sum(10, 20)); // 30

// 첫 번째 인수와 나머지 분리
function introduce(name, ...hobbies) {
    console.log(`이름: ${name}`);
    console.log(`취미: ${hobbies.join(", ")}`);
}

introduce("홍길동", "독서", "영화", "등산");

3️⃣ 전개 연산자 (...spread)

// 배열을 개별 인수로 전개
function add(a, b, c) {
    return a + b + c;
}

let numbers = [1, 2, 3];
console.log(add(...numbers)); // 6

// 배열 복사
let arr1 = [1, 2, 3];
let arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]

4️⃣ 화살표 함수의 this

let obj = {
    name: "객체",
    
    regularFunc: function() {
        setTimeout(function() {
            console.log(this.name); // undefined (this가 바뀜)
        }, 1000);
    },
    
    arrowFunc: function() {
        setTimeout(() => {
            console.log(this.name); // "객체" (this가 유지됨)
        }, 1000);
    }
};

5️⃣ 구조 분해 할당

// 객체에서 필요한 값만 받기
function createUser({name, age, email = "미입력"}) {
    return `${name}(${age}세) - ${email}`;
}

let user = createUser({
    name: "홍길동",
    age: 25
});
console.log(user); // "홍길동(25세) - 미입력"

// 배열에서 값 받기
function getPosition() {
    return [37.5, 127.0]; // 위도, 경도
}

let [lat, lng] = getPosition();
console.log(`위도: ${lat}, 경도: ${lng}`);

6️⃣ 템플릿 리터럴

// 여러 줄 문자열과 변수 삽입
function greet(name, age) {
    return `안녕하세요, ${name}님!
올해 ${age}세이시군요.
내년에는 ${age + 1}세가 되시겠네요.`;
}

console.log(greet("홍길동", 25));

7️⃣ async/await (비동기 처리)

// Promise 방식
function getData() {
    return new Promise(resolve => {
        setTimeout(() => resolve("데이터"), 1000);
    });
}

// async/await 방식 (더 읽기 쉬움)
async function processData() {
    try {
        console.log("데이터 요청 중...");
        let data = await getData();
        console.log(data);
        return "완료";
    } catch (error) {
        console.log("오류:", error);
    }
}

processData(); // "데이터 요청 중..." → (1초 후) "데이터" → "완료"

💡 핵심 정리

  • 기본 매개변수: 인수가 없을 때 기본값 설정
  • 나머지 매개변수: 여러 인수를 배열로 받기
  • 전개 연산자: 배열을 개별 값으로 펼치기
  • 화살표 함수: this가 바뀌지 않음
  • 구조 분해: 객체/배열에서 필요한 값만 추출
  • 템플릿 리터럴: 문자열 안에 변수 삽입
  • async/await: 비동기 코드를 동기처럼 작성

📚 마무리 정리

오늘 배운 내용들을 정리하면:

8.1 함수 정의: 선언문, 표현식, 생성자, 화살표 함수 등 다양한 정의 방법

8.2 함수 호출: 일반 호출, 메서드 호출, 생성자 호출, 간접 호출 방법

8.3 함수 인수: 매개변수 처리, 기본값, 가변 인수 등

8.4 재귀 함수: 자기 자신을 호출하는 함수의 활용

8.5 실행 과정: 실행 컨텍스트, 호이스팅, 스코프 체인

8.6 클로저: 함수와 렉시컬 환경의 조합

8.7 이름 공간: 전역 오염 방지와 모듈 패턴

8.8 객체로서의 함수: 일급 객체로서의 함수 특성

8.9 고차 함수: 함수를 다루는 함수

8.10 콜백 함수: 비동기 처리와 이벤트 처리

8.11 ES6+ 기능: 현대적인 함수 기능들

 

함수는 자바스크립트의 핵심이며, 이러한 개념들을 잘 이해하고 활용하면 더 효율적인 코드를 작성할 수 있습니다.

각 개념들이 서로 연결되어 있으므로, 함수 부분은 좀 더 자세하게 공부를 하는게 좋을 것 같습니다!

💡 오늘 새롭게 알게 된 것

  • 함수에 대한 전반적인 내용은 어느 정도 알고 있었는데, 이번에 책을 읽으면서 실제 사용 방법들을 더 깊이 있게 이해하게 되었습니다

🤔 어려웠던 점

  • 함수의 전반적인 내용이 알아야 할것도 많고 그래서인지 복잡했어요
  • this는 들어봤었음에도 불구하고 볼때마다 새롭게 느껴지는것 같아요

🎯 다음 학습 계획

다음 포스팅에서는 객체에 대해 알아보겠습니다.