프로그래밍에서 함수란 JavaScript에서 기본 구성 요소중 하나입니다. Javascript에서 함수는 작업을 수행하거나 값을 계산하는 명령문의 집합인 프로시저(procedure)와 비슷하나, 프로시저가 함수로 쓰이려면 입력을 반드시 받아야 하고 입력과 명확한 관계가 있는 출력을 반환해야 합니다.
1. 함수 정의
함수 정의(선언)는 다음과 같은 함수 키워드로 구성되어 있습니다.
- 함수의 이름
- 함수의 매개변수들, 괄호로 묶고 쉼표로 구분
- 함수를 정의하는 JavaScript 문으로 중괄호로 묶습니다.
function square(number) {
return number * number;
}
함수 square은 number라는 하나의 매개변수를 가집니다. 이 함수는 함수의 매개변수(number)를 곱한 값을 반환하는 간단한 구문을 가지고 있습니다. return 문은 함수가 반환하는 값을 지정합니다. 기본 적으로 number와 같은 매개변수는 값으로 할당하는 경우에도 변경 사항은 전역적으로 또는 해당 함수를 호출한 코드에 반영하지 않습니다.
객체를 매개 변수로 전달할 때 함수가 객체의 속성을 변경한다면 다음 예제와 같이 함수 외부에 해당 변경 사항을 볼 수 있습니다.
// function square(number) {
// return number * number;
// }
function myFunc(theObject) {
theObject.make = "Toyota";
}
const myCar = {
make: "Hyundai",
model: "Accord",
year: "1999",
};
const x = myCar.make;
myFunc(myCar); // ToyoTa로 변경
console.log(myCar.make);
const y = myCar.make;
배열(Array)을 매개 변수로 전달할 때 함수가 배열의 값을 변경할때 함수 외부에서 해당 변경 사항을 볼 수 있습니다.
function myFunc(theArr) {
theArr[0] = 20;
}
const arr = [45];
console.log(arr[0]); //45
myFunc(arr);
console.log(arr[0]); // 20
2. 함수 표현식
위의 함수 선언은 구문상 명령문이지만 함수는 함수 표현식(function expression)으로도 만들 수 있습니다. 이러한 함수를 익명 함수라 하며, 함수가 이름을 가질 필요는 없음을 의미합니다.
const square = function(number) {
return number * number;
};
const x = square(4); // 'x' 의 값은 16
console.log(x);
하지만 함수 표현식에서도 함수의 이름을 지정할 수 있습니다. 함수가 자신을 참조할 수 있고, 디버거의 스택 추적에서 함수를 보다 쉽게 식별할 수 있습니다.
const factorial = function fac(n) {
return n < 2 ? 1 : n * fac(n - 1);
};
console.log(factorial(3));
함수 표현식은 함수를 다른 함수의 매개변수로 전달할 때 편리합니다.
function map(fn, arr) {
const result = new Array(arr.length);
for (let i = 0; i < arr.length; i++) {
result[i] = fn(arr[i]);
}
return result;
}
함수 표현식으로 정의된 함수를 받고 두 번째 인수로 전달된 배열의 각 요소에 대해 함수를 호출합니다.
function map(fn, arr) {
const result = new Array(arr.length);
for (let i = 0; i < arr.length; i++) {
result[i] = fn(arr[i]);
}
return result;
}
const fn = function(x) {
return x * x * x;
};
const numbers = [0, 1, 2, 5, 10];
const cube = map(fn, numbers);
console.log(cube);
함수는 [0, 1, 8, 125, 1000]을 반환합니다. Javascript에서는 조건에 따라 함수를 정의할 수 있습니다. 예를 들어, 아래 함수는 num이 0인 경우에만 myFunc를 정의합니다.
let myFunc;
if (num === 0) {
myFun = function (theObject) {
theObject.name = "Toyota"
}
}
3. 함수 호출
함수를 정의해도 함수가 실행되는 것은 아닙니다. 함수를 정의하느 것은 함수의 이름과 수행할 작업을 지정합니다. 함수를 호출하면 지정된 매개 변수를 가지고 작업이 수행됩니다. 예를 들어 함수 square를 정의했다면 함수는 다음과 같이 호출합니다.
square(5);
위 문장은 5라는 인수를 가지고 함수를 호출합니다. 함수는 이 함수의 실행문을 실행하고 값 25를 반환합니다. 함수는 호출될 때 스코프 내에 있어야 합니다. 함수의 스코프는 함수가 선언된 곳 입니다.
console.log(square); // undefined
console.log(square(5)); // TypeError : square is not function
square = function(n) {
return n * n;
};
함수의 인수는 문자열과 숫자에 제한되지 않습니다. 함수는 자신을 호출할 수 있습니다. 예를 들어 팩토리얼 재귀적으로 계산하는 함수를 호출할 수 있습니다.
function factorial(n) {
if (n == 0 || n == 1) return 1;
else return n * factorial(n - 1);
}
다음과 같이 1부터 5까지 팩토리얼을 계산할 수 있습니다.
function factorial(n) {
if (n == 0 || n == 1) return 1;
else return n * factorial(n - 1);
}
const a = factorial(1); // 1
const b = factorial(2); // 2
const c = factorial(3); // 6
console.log(c,b,a);
함수를 호출하는 다른 방법들은 함수를 동적 호출해야 하거나, 함수의 인수의 수가 다르거나, 함수 호출의 맥락이 런타임에서 결정된 특정한 객체로 설정해야 하는 경우가 자주 있습니다. 함수가 그 자체로 객체이며 이 객체는 차례로 매서드를 가지고 있습니다.
4.함수 호이스팅
console.log(square(7)); // 49
function square(n) {
return n * n;
}
square 함수가 정의되기 전 호출되었지만 오류 없이 실행됩니다. Javascript 인터프리터가 함수 선언을 현재 스코프의 최상단으로 끌어올려지기 때문에 위 코드는 다음과 같습니다.
// 모든 함수 선언이 최상단으로 끌어올려짐
function square(n) {
return n * n;
}
console.log(square(7)); // 49
함수 호이스팅은 함수 선언에만 적용됩니다. 함수 표현식에선 쓸 수 없습니다.
console.log(square); // Error : 초기화 되기전 접근 X
// 모든 함수 선언이 최상단으로 끌어올려짐
function square(n) {
return n * n;
}
console.log(square(7)); // 49
5. 함수 스코프
함수 내에서 정의된 변수는 변수가 함수의 스코프에서만 정의되어 있어 함수 외부의 어느 곳에서든 접근할 수 없습니다. 그러나 함수가 정의된 스코프 내 모든 변수나 함수에 접근할 수 있습니다. 즉, 전역함수는 모든 전역 범위에 정의된 모든 변수에 접근할 수 있습니다.
다른 함수 내부에 정의 된 함수는 상위 함수에 정의된 모든 변수에 접근할 수 있고, 또한 상위 함수가 접근할 수 있는 다른 함수에도 접근할 수 있습니다.
// 전역 스코프
const num1 = 20;
const num2 = 3;
const name = "Choke";
// 전역 스코프
function multiply() {
return num1 * num2;
}
console.log(multiply()); // 60
function getScore() {
const num1 = 2;
const num2 = 3;
function add() {
return `${name} scored ${num1 + num2}`;
}
return add();
}
console.log(getScore()); // 5
6. 스코프와 함수 스택
함수는 자신을 참조하고 호출할 수 있습니다. 자신을 참조하는 방법은 세 종류가 있습니다.
- 함수의 이름
- arguments.callee
- 함수를 참조하는 스코프 내 변
var foo = function bar() {
}
함수 본문 내 다음은 모두 동일합니다.
- bar()
- arguments.callee()
- foo()
자신을 호출하는 함수를 재귀 함수라합니다.
var x = 0
while (x < 10) {
// x < 10
x++;
}
function loop(x) {
if ( x >= 20) {
return;
loop(x + 1); // 재귀
}
loop(0);
}
일부 알고리즘은 단순 반복 루프가 될 수 없습니다. 예를 들어, DOM과 같은 트리 구조의 모든 노드를 가져오는 것은 재귀를 사용하는 편이 더 간편합니다.
function walkTree(node) {
if (node == null) {
return;
}
for(var i = 0; i < node.childNodes.length; i++) {
walkTree(node.childNodes[i])
}
}
함수 loop와 비교해, 각 재귀 호출 자체는 많은 재귀 호출을 수행합니다. 재귀 알고리즘을 비재귀 알고리즘으로 변환하는 것은 가능하지만 논리가 더 복잡해지는 경우가 있고 스택을 사용해야 합니다. 사실 재귀도 스택(함수 스택)을 사용합니다. 스택과 같은 동작은 다음 예제에서 확인할 수 있습니다.
function foo(i) {
if (i < 0) {
return;
}
console.log(`begin: ${i}`);
foo(i - 1);
console.log(`end: ${i}`);
}
foo(3);
7. 중첩된 함수와 클로저
함수 내에 함수를 끼워 넣을 수 있습니다. 중첩된(내부) 함수는 그것을 포함하는 (외부) 함수에서만 사용할 수 있습니다. 그것은 또한 클로저를 형성합니다. 클로저는 변수를 바인딩하는 환경과 함께 자유 변수를 가질 수 있는 표현입니다.
function addSqaures(a,b) {
function square(x) {
return x * x;
}
return square(a) + square(b);
}
a = addSqaures(2,3); // 13
b = addSqaures(3, 4); // 25
c = addSqaures(4, 5); // 41
console.log(a,b,c);
내부 함수는 클로저를 형성하기 때문에 외부 함수를 호출하고, 외부 및 내부 함수 모두에 인수를 지정할 수 있습니다.
function outside(x) {
function inside(y) {
return x + y;
}
return inside;
}
fn_inside = outside(3);
result = fn_inside(5); // 8
result1 = outside(3)(5); // 8
console.log(fn_inside, result, result1);
8. 변수의 보존
8-1. 다중 중첩함수
클로저는 여러 스코프가 포함될 수 있으며, 이스코프를 포함하는 함수의 스코프를 재귀적으로 포함합니다.
function A(x) {
function B(y) {
function C(z) {
console.log(x + y + z);
}
C(3);
}
B(2);
}
A(1); // logs 6 (1 + 2 + 3)
8-2. 이름 충돌
클로저의 스코프에서 두 개의 인수 또는 변수의 이름이 같은 경우, 이름 충돌이 발생합니다. 이 경우 더 안쪽 스코프가 우선순위를 갖습니다. 가장 바깥 스코프는 우선순위가 가장 낮은 반면에, 가장 안쪽 스코프는 가장 높은 우선순위를 갖습니다. 이것이 스코프 체인(scope chain)입니다. 체인의 첫번째 가장 안쪽 스코프이고, 마지막은 가장 바깥쪽 스코프입니다.
function outside() {
var x = 10;
function inside(x) {
return x * 2;
}
return inside;
}
result = outside()(10); // 10대신 20 반환
console.log(result);
8-3. 클로저
클로저는 JavaScript의 강력한 기능 중 하나입니다. 함수의 중첩(함수 안에 함수를 정의하는 것)을 허용하고, 내부 함수가 외부 함수 안에서 정의된 모든 변수와 함수(및 외부함수가 접근 할 수 있는 다른 모든 변수와 함수들까지)에 대해 전체 접근 권한을 부여합니다.
하지만 외부 함수는 내부 함수에 정의된 변수와 함수에 접근 할 수 없습니다. 이는 내부 함수의 변수에 대한 일종의 캡슐화를 제공합니다. 또한, 내부 함수는 외부 함수의 스코프에 접근할 수 있기 때문에, 내부 함수가 외부 함수의 수명 보다 오래 생존하는 경우 외부 함수에서 선언된 변수나 함수는 외부 함수의 실행 기간보다 오래 유지됩니다.
const pet = function (name) {
// 외부 함수는 'name' 이라 불리는 변수를 정의
const getName = function() {
return name; // 내부 함수는 외부 함수의 'name' 변수에 접근합니다.
};
return getName; // 내부 함수를 반환, 외부 스코프 노출
};
const myPet = pet("Vest");
console.log(myPet()); // :Vest
8-4. 인수 객체 사용
함수의 인수는 배열과 비슷한 객체로 처리됩니다. 함수 내 전달 된 인수를 다음과 같이 다룰 수 있습니다.
arguments[i];
function myCrost(separator) {
let result = "";
for(let i = 1; i < arguments.length; i++) {
result += arguments[i] + separator;
}
return result;
}
const output = myCrost("-", "apple", "banana", "cherry");
console.log(output);
어떤 개수의 인수도 이 함수로 넘겨줄 수 있고, 이 함수는 각각의 인수를 하나의 문자열 "리스트" 로 연결합니다.
9. 함수의 매개 변수
ECMAScript 2015에서 추가된 매개변수 구문(parameter syntax)에는 기본 매개변수와 나머지 매개변수라는 두 가지 특수한 유형이 있습니다.
9-1. 기본 매개변수
JavaScript에서 함수의 매개변수는 undefined 가 기본으로 설정됩니다. 하지만 경우에 따라 다른 기본값을 설정하는 것이 유용할 수 있습니다.
function multiply(a, b) {
b = typeof b !== "undefined" ? b : 1;
return a * b;
}
console.log(multiply(5)); //5
디폴트 매개변수를 사용한다면 함수 본문에서 일일히 확인할 필요가 없습니다. 간단히 b의 기본값으로 1을 넣어주면 됩니다.
function multiply(a, b = 1) {
return a * b;
}
console.log(multiply(5)); //5
9-2. 나머지 매개변수
나머지 매개변수 구문을 사용하면 배열로 임의 개수의 인수로 나타낼 수 있습니다.
function multiply(multiplier, ...theArgs) {
return theArgs.map((x) => multiplier * x);
}
var arr = multiply(2,1,2,3);
console.log(arr); // [2,4,6]
10.화살표 함수
화살표 함수 표현은 함수 표현식에 비해 짧은 문법을 가지고 있고 사전적으로 this 값을 묶습니다. 화살표 함수는 언제나 익명입니다. 화살표 함수 도입에는 더 짧은 함수와 바인딩 되지않은 this라는 두 가지 요인이 영향을 미쳤습니다.
더 짧은 함수
function multiply(multiplier, ...theArgs) {
return theArgs.map((x) => multiplier * x);
}
var arr = multiply(2,1,2,3);
console.log(arr); // [2,4,6]
var a = ["Hydrogen", "Helium", "Lithium", "Berllium"];
var a2 = a.map(function (s) {
return s.length;
});
console.log(a2);
var a3 = a.map((s) => s.length);
console.log(a3);
사전적 this
function Person() {
// this 사전적 정의
this.age = 0;
setInterval(function growUp() {
this.age++;
}, 1000);
}
var p = new Person();
ECMAScript 3/5 에서 이 문제는 this 안의 값을 뒤덮을 수 있는 변수에 할당하며 해결했습니다.
function Person() {
var self = this // `self ` 대신 `that`를 선택하는 사람도 있음
self.age = 0;
setInterval(function growUp() {
self.age++;
},1000);
}
또는 적절한 this 값으 growUp() 함수에 전달되도록, 바인딩된 함수를 만들 수도 있습니다. 화살표 함수에는 this가 없습니다. 화살표 함수를 포함하는 객체 값이 사용됩니다. 따라서 다음 코드에서 setInterval에 전달 된 함수 내의 this 값은 화살표 함수를 둘러싼 함수의 this와 같은 값을 갖습니다.
function Person() {
var self = this // `self ` 대신 `that`를 선택하는 사람도 있음
self.age = 0;
setInterval(function growUp() {
self.age++;
},1000);
var p = new Person();
}
참고 자료
함수 - JavaScript | MDN
함수는 JavaScript에서 기본 구성 요소 중 하나입니다. JavaScript의 함수는 작업을 수행하거나 값을 계산하는 명령문의 집합인 프로시저(procedure)와 비슷하지만, 프로시저가 함수로 쓰이려면 입력을
developer.mozilla.org
GitHub - javascript-only/javascript-bloging
Contribute to javascript-only/javascript-bloging development by creating an account on GitHub.
github.com
'Front-End > JavaScript' 카테고리의 다른 글
[Javascript] 6강 숫자와 날짜 (0) | 2025.02.06 |
---|---|
[Javascript] 5강 표현식과 연산자 (2) | 2025.02.06 |
[Javascript] 3강 제어문 (0) | 2025.02.05 |
[Javascript] 2강 자바스크립트 문법과 자료형 (0) | 2025.02.05 |
[Javascript] 1강 자바스크립트란 무엇인가? (1) | 2025.02.05 |