JavaScript - 함수 (복습)
2024. 3. 10. 20:38ㆍ개인노트-개인공부
JavaScript - 함수 (복습)
목차
선언과 표현 그리고 호이스팅
매개변수 패턴
화살표 함수
즉시실행함수(IIFE)
콜백
재귀
호출 스케줄링
# 선언과 표현 그리고 호이스팅
- 함수 선언문과 함수 표현식의 차이
- function 키워드로 시작하는지? const 나 let으로 선언된 변수에 할당 연산자를 통해서 함수가 들어가는지의 차이.
// 함수(Function)
// 함수 선언문 (Declaration)
function hello() {}
// 함수 표현식 (Expression)
const hello = function() {}
## 호이스팅
- 함수 선언에서는 호이스팅이라는 현상이 발생합니다.
- 함수가 선언되어져 있는 코드를 선언된 부분 안(유효한 범위 안)에서 제일 위로 끌어 올려서 동작하는 개념입니다.
// 호이스팅 (Hoisting)
function hello() {
console.log('Hello~');
}
hello(); // Hello~
// ------------------------------------
// 위(hello)와 아래(hello2)의 함수는 모두 잘 동작합니다.
hello2(); // Hello~
function hello2() {
console.log('Hello~');
}
만약 아래처럼 작성하면 ReferenceError가 발생합니다.
hello();
// 호이스팅 되지 않음
const hello = function () {
console.log('Hello~');
}

추가로 아래처럼 작성하면,
// world(); // error : 초기화되기 전에 접근 불가
const world = function hello() {
console.log('Hello~');
}
// hello(); // error : hello를 찾을 수 없음
world(); // Hello~
## 정리
- 간단하게 생각해보면, 익명함수에 상관없이 어떤 함수이든 할당연산자를 통해서 변수에 함수를 할당하면 함수 표현식이되고
- 할당 연산자를 사용하지 않고 function 키워드를 사용해서 이름이 있다면 함수 선언문이 됩니다.
- 함수 선언문은 호이스팅 현상이 발생합니다.
# 매개변수 패턴
## 기본값
function sum(a, b) {
return a + b;
}
console.log(sum(1, 2)); // 3
console.log(sum(7)); // NaN // 두번째 매개변수에는 값이 들어가지 않음
// 기본값 지정
function sum(a, b = 1) {
return a + b;
}
console.log(sum(1, 2)); // 3
console.log(sum(7)); // 8
## 구조 분해 할당(객체)
const user = {
name: 'USER1',
age: 85,
}
function getName(user) {
const { name } = user;
return user.name;
}
console.log(getName(user)); // USER1
// 또는 아래처럼 작성 가능
function getName({ name }) {
return user.name;
}
// 기본값 지정을 활용 가능
function getEmail({ email = '이메일이 없습니다.'}) {
return user.email
}
console.log(getEmail(user)); // undefined가 나오지 않고, '이메일이 없습니다.'가 출력됨.
## 구조 분해 할당(배열)
const fruits = ['Apple', 'Banana', 'Cherry'];
const numbers = [1, 2, 3, 4, 5, 6, 7];
function getSecondItem([a, b, c]) {
return b;
}
console.log(getSecondItem(fruits)); // Banana
// 또는 아래처럼 사용하지 않는 매개변수는 생략하여 작성 가능
function getSecondItem2([, b]) {
return b;
}
console.log(getSecondItem2(numbers)); // 2
## 나머지 매개변수(전개 연산자)
개념 1
// 들어오는 인수들을 순서대로 받아서 배열로 저장하게 됩니다.
function sum(...rest) {
console.log(rest);
}
console.log(sum(1, 2)); // [1, 2]
console.log(sum(1, 2, 3, 4)); // [1, 2, 3, 4]
console.log(sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
개념 2
// 들어오는 인수들을 순서대로 받아서 배열로 저장하게 됩니다.
// 첫번째 인수(a)와 두번째 인수(b)를 제외한 나머지인수를 rest로 받아서 배열로 저장합니다.
function sum(a, b, ...rest) {
console.log(rest);
}
console.log(sum(1, 2)); // []
console.log(sum(1, 2, 3, 4)); // [3, 4]
console.log(sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); // [3, 4, 5, 6, 7, 8, 9, 10]
실습 1
// 들어오는 인수들을 순서대로 받아서 배열로 저장하게 됩니다.
// 첫번째 인수(a)와 두번째 인수(b)를 제외한 나머지인수를 rest로 받아서 배열로 저장합니다.
function sum(...rest) {
rest.reduce(function (acc, cur) {
return acc + cur
}, 0);
}
console.log(sum(1, 2)); // 3
console.log(sum(1, 2, 3, 4)); // 10
console.log(sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); // 55
arguments
- 위처럼 나머지 매개변수를 이용해서 인수를 받지 않아도, 들어오는 모든 인수를 가지고 있는 이미 선언되어있는 객체가 있다.
// 들어오는 인수들을 순서대로 받아서 배열로 저장하게 됩니다.
// 첫번째 인수(a)와 두번째 인수(b)를 제외한 나머지인수를 rest로 받아서 배열로 저장합니다.
function sum(...rest) {
console.log(arguments);
rest.reduce(function (acc, cur) {
return acc + cur
}, 0);
}
console.log(sum(1, 2)); // 3
console.log(sum(1, 2, 3, 4)); // 10
console.log(sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); // 55

- arguments는 함수안에서 언제든지 사용할 수 있는 객체입니다.
- arguments는 배열이 아니고 유사배열(Array-Like)이기 때문에 reduce와 같은 배열함수를 사용할 수 없습니다.
# 화살표 함수
기존의 function 키워드 함수보다 훨씬 더 단순하고 간결하게 함수 문법을 작성할 수 있습니다.
const sum = () => {} // 화살표 함수
예시 코드
const sum = () => {
return a + b;
}
console.log(sum(1, 2)); // 3
console.log(sum(10, 20)); // 30
# 즉시실행함수(IIFE)
- 일반적으로 함수를 만들면서 호출하기 위해서 함수 이름을 부여합니다.
- 만약, 함수의 이름없이 바로 실행되기를 원한다면, 즉시실행함수 문법을 통해서 익명함수를 만들고 바로 실행할 수 있습니다.
예시 코드
const a = 7;
const double = () => {
console.log(a * 2);
}
double();
// 즉시 실행 함수
(() => {
console.log(a * 2);
})();
즉시실행함수의 다양한 패턴
// 아래 5개의 패턴 모두 같은 동작을 합니다.
;(() => {})() // (F)()
;(function () {})() // (F)()
;(function () {}()) // (F())
;!function () {}() // !F()
;+function () {}() // +F()
또 다른 개념
;((a, b) => {
console.log(a)
console.log(b)
})(1, 2)
//console 출력 결과
// 1
// 2
- 두 번째 소괄호에 들어가는 각각의 데이터들을 즉시 실행하는 해당 함수에 매개변수로 전달해 줍니다.
- 이것을 사용하면 다양한 전역데이터들의 이름을 간소화할 수 있습니다.
- 예를 들면...
;((a, b) => {
console.log(a)
console.log(b)
})(window, document)

- window객체와 document객체를 매개변수로 전달해주었지만, 즉시실행함수 내부에서는 a와 b라는, 해당 변수가 무엇을 의미하는지 전혀 유추할 수 없는 이름으로 바꿔서 사용할수 있습니다.
- 이 방식으로 해당하는 코드를 난독화할 수 있습니다.
# 콜백
- 함수가 실행될 때, 인수로 들어가는 또 하나의 함수
- 함수도 또 하나의 데이터
예제1
const a = callback => {
console.log('A');
callback();
}
const b = () => {
console.log('B');
}
a(b);
// 출력 결과
// A
// B
예제2
const sum = (a, b) => {
setTimeout(() => {
return a + b
}, 1000);
}
console.log(sum(1, 2)); // undefined
console.log(sum(3, 7)); // undefined
// 위와 같은 경우에 의도한 대로 a + b가 반환되지 않고 undefined가 반환됩니다.
// 이런 경우 아래 처럼 콜백을 활용한다면..
const sum = (a, b, c) => {
setTimeout(() => {
c(a + b)
return a + b
}, 1000);
}
sum(1, 2, value => {
console.log(value)
}) // 3
sum(3, 7, () => {
console.log(value)
}) // 10
좀 더 유용한 예제
이미지를 load해서 출력하는 예제
<div class="container">
<h1>Loading...</h1>
</div>
// https://www.gstatic.com/webp/gallery/4.jpg
const loadImage = (url, cb) => {
const imgEl = document.createElement('img');
imgEl.src = url;
imgEl.addEventListener('load', () => {
cb(imgEl);
}); // 이미지의 load가 끝나면 콜백이 실행됨
}
const containerEl = document.querySelector('.container');
loadImage('https://www.gstatic.com/webp/gallery/4.jpg', (imgEl) => {
containerEl.innerHTML = '';
containerEl.append(imgEl);
}); // 두 번째 인수로 콜백을 추가함
# 재귀
- 재귀 (Recursive)
- 함수 안에서 함수 자기 자신을 다시 호출해서 사용하는 방법을 의미합니다.
// 재귀 함수.
// 아래 함수는 무한 동작합니다.
const a = () => {
console.log('A');
a();
}
a();
- 재귀는 기본적으로 무한 동작하기 때문에, 필요에 따라서 멈춰줄 수 있어야 합니다.
// 재귀 함수.
let i = 0;
const a = () => {
console.log('A');
i += 1;
if (i < 4) {
a();
}
}
a();
// 출력결과
// A
// A
// A
// A
예제
const userA = { name: 'A', parent: null };
const userB = { name: 'B', parent: userA };
const userC = { name: 'B', parent: userB };
const userD = { name: 'B', parent: userC };
const getRootUser = user => {
if (user.parent) {
return getRootUser(user.parent);
}
return user;
}
console.log(getRootUser(userD)); // { name: 'A', parent: null }
console.log(getRootUser(userC)); // { name: 'A', parent: null }
console.log(getRootUser(userB)); // { name: 'A', parent: null }
console.log(getRootUser(userA)); // { name: 'A', parent: null }
재귀 함수는 무한으로 반복 실행하면서 특정 조건을 찾을 수 있는 구조이기 때문에 종종 아주 유용하게 사용할 수 있습니다.
# 호출 스케줄링
## setTimeout
<h1>Hello world!</h1>
const hello = () => {
console.log('Hello~');
}
const timeout = setTimeout(hello, 2000);
const h1El = document.querySelector('h1');
h1El.addEventListener('click', () => {
console.log('Clear!');
clearTimeout(timeout);
});
## setInterval
const hello = () => {
console.log('Hello~');
}
const timeout = setInterval(hello, 2000);
const h1El = document.querySelector('h1');
h1El.addEventListener('click', () => {
console.log('Clear!');
clearInterval(timeout);
});
'개인노트-개인공부' 카테고리의 다른 글
form 사용 유용한 팁 (1) | 2024.05.28 |
---|---|
html 문서 로드와 이벤트 (1) | 2024.05.12 |
Javascript - this (1) | 2024.02.04 |
Node vs Element (2) | 2024.01.28 |
javascript - 클로저 (1) | 2024.01.21 |