개요
저번 글에서는 JavaScript에 대한 기초적인 내용을 정리했는데 이번에는 함수에 대해 알아봐야겠다.
1. 함수란?
함수는 특정 작업을 수행하는 코드 블록이고, 필요할 때마다 호출해서 실행할 수 있다. 즉, 함수를 사용한다면 여러 곳에서 사용되는 기능들을 작성하는 반복적인 작업을 줄일 수 있겠지?!
function 함수이름 (매개변수1, 매개변수2, ...) {
실행될 기능
}
-> 기본적인 모양은 위와 같다.
1-1. 함수 선언 방법
함수 선언문
function add(a, b) {
return a + b;
}
console.log(add(2, 3)); // 5
-> function 키워드로 선언하고, 호이스팅의 영향을 받아 함수 선언 전에도 호출할 수 있다.
* 호이스팅(Hoisting)
-> 변수 및 함수 선언이 코드 실행 전에 메모리에 저장되는 동작을 의미한다.
-> 선언된 변수와 함수가 코드의 최상단으로 끌어올려진 것처럼 동작하는 현상이다.
sayHello(); // ✅ 정상 실행: "Hello, world!"
function sayHello() {
console.log("Hello, world!");
}
-> 이렇게 동작할 수 있는 이유는 자바스크립트 엔진이 실행하기 전에 함수를 메모리에 먼저 등록하기 때문이다.
-> But, 밑에서 정리할 함수 표현식은 호이스팅되지 않는다.
sayGoodbye(); // ReferenceError: Cannot access 'sayGoodbye' before initialization
const sayGoodbye = function() {
console.log("Goodbye!");
};
// 함수 표현식
함수 표현식
-> 변수에 익명 함수를 할당하는 방식으로, 호이스팅의 영향을 받지 않는다.
const subtract = function(a, b) {
return a - b;
};
console.log(subtract(5, 3)); // 2
화살표 함수
-> function 키워드를 사용하지 않고 => 를 활용해, 작성할 수 있다.
const multiply = (a, b) => a * b;
console.log(multiply(3, 4)); // 12
1-2. 함수의 매개변수
자바스크립트에서는 매개변수에 대한 데이터 유형을 지정하지 않는다. 또한 자바스크립트 함수는 전달된 매개변수에 대해 유형 검사를 하지 않고, 매개변수의 수 또한 확인하지 않는다.
매개변수의 개수가 많다면 초과된 매개변수는 'undefined'로 지정되고, 초과되었다면 초과된 매개변수는 무시된다.
function sayHello(name) {
console.log("Hello, " + name);
}
sayHello(); // Hello, undefined
기본 매개변수
function greet(name = "Ho") {
return `안녕, ${name}!`;
}
console.log(greet()); // 안녕, Ho!
console.log(greet("Taek")); // 안녕, Taek!
-> 매개변수가 전달되지 않았을 때에는 기본값을 지정할 수 있다.
객체나 배열
function printUser({ name, age }) {
console.log('이름: ${name}, 나이: ${age}');
}
printUser({ name : "Ho", age: 2 });
-> 또한 객체나 배열을 매개변수로 받아서 내부 값에 직접 접근할 수도 있다.
나머지 매개변수
-> 함수의 매개변수가 몇 개가 될지 모를 때, 여러 개의 인자를 배열 형태로 받을 수 있도록 하는 기능이다.
function myFunction(...args) {
console.log(args);
}
myFunction(1, 2, 3, 4, 5); // [1, 2, 3, 4, 5]
-> 여기서 중요한 점이 배열 형태로 받을 수 있다는 점과 만약 다른 일반 매개변수가 있다면, 나머지 매개변수는 맨 마지막에 와야 한다는 점이다.
function sum(first, second, ...rest) {
console.log("첫 번째 숫자:", first);
console.log("두 번째 숫자:", second);
console.log("나머지 숫자들:", rest);
}
sum(10, 20, 30, 40, 50);
// 첫 번째 숫자: 10
// 두 번째 숫자: 20
// 나머지 숫자들: [30, 40, 50]
1-3. 콜백 함수
-> 다른 함수의 인자로 전달되어 실행되는 함수를 의미한다.
-> 주로 비동기 처리, 이벤트 핸들링, 배열 메서드 등에서 자주 사용된다.
function greet(name, callback) {
console.log(`안녕, ${name}`);
callback();
}
function afterGreeting() {
console.log("반가워유");
}
greet("Ho", afterGreeting);
// 안녕, Ho
// 반가워유
-> 위의 코드에서 greet 함수는 callback 함수를 인자로 받아, name을 출력한 뒤 콜백 함수를 실행한다.
-> 주의할게, 콜백 함수는 중첩이 많아지면 콜백 지옥!!이 발생할 수 있기 때문에 주의해서 사용해야 할 필요가있다.
setTimeout(() => {
console.log("작업 1 완료");
setTimeout(() => {
console.log("작업 2 완료");
setTimeout(() => {
console.log("작업 3 완료");
}, 1000);
}, 1000);
}, 1000);
-> 보기 어려워...
1-4. 클로저(Closure)
클로저는 함수가 생성될 때 그 함수가 선언된 환경(Lexical Environment)을 기억하고, 이후 그 환경에 접근할 수 있는 기능을 의미한다.
즉, 함수가 자신이 선언된 스코프(scope) 밖에서도 그 스코프의 변수에 접근할 수 있도록 해주는 개념이다.
일단 Lexical Environment라는 말 자체가 이해가 안가는데 하나씩 알아보자.
* Lexical Environment
-> 변수와 함수의 스코프(scope)를 관리하는 방식을 의미한다.
-> JavaScript에서 함수나 블록이 선언될 때의 문맥(lexical context)을 기반으로 변수를 검색하는 메커니즘을 설명하는 개념이다.
여기서 스코프(scope)라는 것이 '킥'인 느낌이다.
Lexical Environment는 두 가지 구성 요소를 가지는데,
환경 레코드(Environment Record)
-> 변수와 함수 선언을 저장하는 객체
외부 렉시컬 환경(Outer Lexical Environment Reference)
-> 상위 스코프에 대한 참조
동작원리
var globalVar = "I am global";
function outerFunction() {
var outerVar = "I am outer";
function innerFunction() {
var innerVar = "I am inner";
console.log(innerVar); // "I am inner"
console.log(outerVar); // "I am outer"
console.log(globalVar); // "I am global"
}
innerFunction();
}
outerFunction();
-> 이 코드가 실행될 때 전역 실행 컨텍스트(Global Execution Context)가 생성되고, 여기에 대한 Lexical
Environment가 생성된다.
-> 이 코드가 실행되는 과정을 단계별로 나눌 수 있다.
1) 전역 Lexical Environment
-> globalVar 변수 저장
-> outerFunction 함수 저장
2) outerFunction 실행 시
-> 새로운 Lexical Environment 생성
-> outerVar 저장
-> 전역 Lexical Environment를 참조
3) innerFunction 실행 시
-> 새로운 Lexical Environment 생성
-> innerVar 저장
-> outerFunction의 Lexical Environment를 참조 -> OuterVar 접근 가능
-> 전역 Lexical Environment 참조 -> globalVar 접근 가능
스코프 체인에 대해서도 알아보면
function outer() {
let a = 10;
function inner() {
let b = 20;
console.log(a); // outer의 Lexical Environment에서 찾음 → 10
console.log(b); // inner의 Lexical Environment에서 찾음 → 20
}
inner();
}
outer();
-> inner()가 실행되면 새로운 Lexical Environment가 생성된다.
-> b는 현재 Lexical Environment에서 찾을 수 있지만, a는 없기 때문에 outer의 Lexical Environment에서 검색한다.
다시 클로저로 돌아와서 이 Lexical Environment로 부터 클로저의 핵심이 등장하게 된다.
'함수가 실행된 이후에도 외부 함수의 변수를 계속 기억하는 것'
function makeCounter() {
let count = 0; // 외부 함수의 변수 (클로저로 유지됨)
return function () { // 내부 함수 (클로저)
count++; // count 값을 1 증가
console.log(count);
};
}
const counter = makeCounter(); // 클로저 생성
counter(); // 1
counter(); // 2
counter(); // 3
1) makeCounter() 실행
-> 새로운 Lexical Environment가 만들어지며 'count = 0'이 저장된다.
2) return function() 실행
-> 내부 함수가 반환되면서 count 변수를 기억하는 클로저가 생성된다.
-> Lexical Environment가 사라지지 않고 유지된다.
3) counter() 실행시
-> count++가 실행될 때 이전의 count 값을 기억하고 유지한다.
-> 그리고 count가 계속해서 증가하게 된다.
'Language > JavaScript' 카테고리의 다른 글
[JavaScript] 기초 정리 (0) | 2025.02.25 |
---|