*해당 포스팅은 김기수의 '코딩 자율학습 HTML + CSS + 자바스크립트', 정재남의 '코어 자바스크립트' 도서를 참고했습니다.*
함수
함수 4 函數
명사 수학 두 개의 변수 x, y 사이에서, x가 일정한 범위 내에서 값이 변하는 데 따라서 y의 값이 종속적으로 정해질 때, x에 대하여 y를 이르는 말.
y가 x의 함수라는 것은 y=f(x)로 표시한다.
자바스크립트에서 함수(function)는 어떤 목적을 가지고 작성한 코드를 모아 둔 블록문이다. 코드를 함수로 만들면 함수를 호출해 함수 내부에 모아 둔 여러줄의 코드를 한 번에 실행할 수 있다. 그러므로 재사용이 편하다.
그림으로 예를 들어 보겠다.
아이디어가 샘솟는 사업가가 있다. 이는 도자기를 생산해 판매할 생각이라 점토를 많이 사들였다. 대량 생산을 위해 도자기를 만들고, 굽고, 무늬를 찍어 낼 공장이 필요할 것이다. 알아봤더니, 공장이 3개가 있었다. 그런데 각 공장은 하나의 일만 할 수 있다. 3가지 일을 모두 해야 비로소 판매할 수 있는 도자기가 만들어지는데 한번에 일을 처리할 수 없어 도자기를 옮기는 과정에만 교통비가 많이 든다. 즉 비효율적이다.
사업가는 돈을 좀 더 들여 도자기 생산 공장을 차렸다. 이 공장에서는 도자기를 만들고, 굽고, 무늬를 찍고 완성된 도자기를 한 번에 생산할 수 있다. 더이상 불필요하게 도자기를 옮길 교통비가 생기지 않는다. 또한, 도자기의 인기가 떨어져 생산할 필요가 없다해도 상관없다. 재료를 조금 바꾸고, 무늬를 꽃에서 풀잎으로 바꾼 그릇을 생산해도 좋다. 즉, 재사용성으로 유지 보수가 엄청나게 편리해진다.
자바스크립트 함수도 마찬가지다. 어떤 목적을 가지고 작성한 코드를 모아 둔 블록문이다.
function plant(){
console.log("도자기 만들기");
console.log("도자기 굽기");
console.log("도자기 무늬 찍기);
}
도자기의 예를 코드로 작성해봤다. 이처럼 블록문을 function 키워드, 식별자, 소괄호와 함께 묶으면 함수가 생성되는데 이를 함수를 정의한다고 한다. 함수의 종류는 2가지가 있는데 ,
순수함수
순수함수는 주어진 입력이 동일하다면 항상 같은 값을 리턴하는 함수를 의미한다. 함수의 출력은 오직 입력에만 의해서 결정되며, 외부 요인에 의해 변하지 않고 예측 가능하니 안정성을 보장할 수 있다.
let z = 3;
function plus (x,y){
console.log(x+y);
}
위와 같은 예시가 있을 때 z가 함수 plus에 영향을 주어선 안 된다. 확실히 결과가 보장되어 있기 때문에 예외 상황 발생이 많지 않아서 좋지만, 유연하게 사용할 수 없고 입력받는 값으로만 처리해야 해 작성하기 어렵다는 단점이 있다. 순수함수가 많아지면 예상하지 못한 오류가 발생할 수도 있다.
일급함수
일급 함수는 함수를 다루는데 있어서 변수와 동일하게 다루는 방식을 의미한다. 예를 들어 함수를 다른 함수의 매개 변수로 사용하거나, 함수가 함수를 반환할 수 있고 변수에도 할당될 수 있다.
let z = 3;
function plus (x,y){
console.log(x+y+z);
}
위 예제에서 z를 함수 plus에서 더해 출력하는 것을 볼 수 있다. 외부 요인이 함수의 기능에 변화를 줬기 때문에 순수함수가 아닌 일급함수. 일급 함수를 지원하는 언어는 함수를 매우 유연하게 사용할 수 있다. 다양한 프로그래밍 기법과 패턴이 가능해지기 때문이다.
example1 - 콜백 함수를 이용한 비동기 프로그래밍
JavaScript에서 비동기 작업을 처리할 때 일급 함수의 개념을 사용해 콜백 함수를 넘겨 비동기 작업이 완료된 후의 로직을 실행할 수 있다.
💡비동기 처리란?(asynchronous)
자바스크립트에서 비동기 처리란 특정 코드가 끝날때까지 코드의 실행을 멈추지 않고 다음 코드를 먼저 실행하는 것을 의미한다. 자바스크립트는 single thread이기 때문에 작업을 하나씩만 수행할 수 있다.
🔍자바스크립트는 동기식 언어
자바스크립트는 동기식 언어다. 하나의 한가지 작업만 가능하다. 자바스크립트에는 Memory Heap과 Call Stack이 존재한다.
- Memory Heap: 변수와 객체의 메모리 할당을 담당
- Call Stack: 함수가 호출이 되면 쌓이는 곳, stack과 동일하게 작동한다. 후입선출.
Call Stack에서 함수가 하나씩 pop되어 실행되기 때문에, 비동기식 처리가 불가능하다. 하지만 비동기식 처리는 꼭 필요하다. 웹 페이지가 로딩되거나 동작하나가 30초 이상 걸린다면 사용자들은 웹 사이트를 사용하지 않을 것이다. 다행히도, 브라우저에서의 자바스크립트 실행 환경에서는 자바스크립트 엔진이 제공하지 않는 DOM 조작이나 AJAX같은 비동기 처리를 위한 web API를 제공한다. (이 내용은 길어질 것 같으므로 후에 포스팅하여 첨부하겠다.)
자바스크립트는 비동기 내장함수를 제공하는데, XMLHttpRequest, fetch(), 아래와 같은 setTimeout이 있다.
setTimeout(function(){ console.log("Hello"); }, 0); console.log("안녕하세요"); //안녕하세요 -> Hello
setTimeout은 특정 코드를 바로 실행하지 않고 일정 시간동안 지연시킨 후 실행한다.
다시 돌아와 일급 함수의 콜백 함수 예제를 보자.
function asyncAdd(a, b, callback) {
setTimeout(() => {
const sum = a + b;
callback(sum);
}, 1000);
}
asyncAdd(5, 7, sum => {
console.log(`Async Sum: ${sum}`); // 12
});
짚고 넘어가야 할 것이있다. 콜백 함수가 무엇일까? 예제를 보면, asyncAdd라는 함수를 선언한 걸 볼 수 있다. 이 함수는 매개변수로 a, b와 callback을 받는다. 아래 문단에서 asyncAdd를 5,7,sum이라는 함수를 매개변수로 넘겨주며 호출했다. callback은 '부르다', '호출하다'는 의미인 call과 '뒤돌아오다', '되돌다'는 의미인 back의 합성어로, '되돌아 호출해달라'라는 명령이다. 즉, 함수 asyncAdd를 호출하면서 특정 조건일 때 함수 sum을 실행해서 알려달라는 것.
본론으로 돌아오자면, 위 예제에서 비동기 작업을 처리할 때 일급 함수의 개념을 사용해 콜백 함수를 넘겼다. 일급 함수는 함수를 매개변수나 변수 취급할 수 있다고 했다.
example2 - 배열 메소드와 콜백 함수
const numbers = [1, 2, 3, 4, 5];
const addFive = numbers.map(number => number + 5); // 각 숫자에 5를 더함
console.log(addFive); // [6, 7, 8, 9, 10]
이 예제도 마찬가지로 map의 인자로 number 함수를 받은 걸 알 수 있다. 배열의 모든 원소에 접근하는 시점에 number 함수를 콜백했다.
example3 - 함수를 반환하는 함수
function createAdder(x) {
return function(y) {
return x + y;
};
}
const addTen = createAdder(10);
console.log(addTen(5)); // 15
const addTwenty = createAdder(20);
console.log(addTwenty(5)); // 25
createAdder 함수를 보자. x를 매개변수로 받고, 함수 하나를 반환한다. 이 함수는 y를 매개변수로 받아 x와 y를 합한 값을 다시 반환한다. y는 어디에서 받을 수 있을까?
addTen 변수를 보자. createAdder에 매개변수 10을 주며 호출했다. 아마 addTen 함수에는 createAdder에서 반환한 함수를 가지고 있을 것이다. 그런데 여기서 궁금한 점이 생겼다.
❓ 매개변수(x) 로 줬던 10은 어디에 있는걸까?
바로 위에서 기술했듯이 addTen 함수에는 createAdder가 반환한 함수가 있을 것이라는 건 예상 가능하다.
그러나 createAdder를 호출할 때 매개변수로 줬던 10은 어디에 저장되어 있는걸까?
이 개념을 알기 위해서는 Closure(클로저)를 알아야 할 것 같다.
도서에 적힌 클로저의 개념이다.
`여러 함수형 프로그래밍 언어에서 등장하는 보편적인 특성이다. 자바스크립트의 고유 개념이 아니라서 명세에서도 클로저의 정의를 다루지 않고 있다. 다양한 문헌에서 제각각 클로저를 다르게 정의하거나 설명한다.`
요약하자면 클로저는 명확하게 설명하기 어려운 개념인 것 같다. 나는 도서의 개념을 익히기로 했다.
MDN에 따르면, 클로저는 함수와 그 함수가 선언될 당시의 lexical environment의 상호관계에 따른 현상이라고 한다. 내용이 길어져 따로 빼고 싶었지만 lexical environment을 알기 위해선 실행 컨텍스트의 개념을 짚고 넘어가야 할 듯 싶다.
내용이 너무 길어지니 이쯤 마무리하고 다음 포스팅 때 자세히 담겠다.
어쨌든, 클로저란 어떤 함수 A에서 선언한 변수 a를 참조하는 내부함수 B를 외부로 전달할 경우 A의 실행 컨텍스트가 종료된 이후에도 변수 a가 사라지지 않는 현상을 말한다.
함수 선언문과 함수 표현식
- 함수 선언문
함수를 새롭게 정의할 때 쓰이는 방식으로, 함수 선언문은 function 정의부만 존재하고 별도의 할당 명령이 없는 것을 의미한다. 또한, 반드시 함수명이 정의되어 있어야 한다(기명 함수 표현식).
- 함수 표현식
함수 표현식은 선언문과 다르게 function을 별도의 변수에 할당하는 것을 말한다. 함수 표현식은 함수명이 없어도 된다. 이를 익명 함수 표현식이라한다.
function a(){}
a(); //ok
var b = function(){}
b(); //ok
var c = function d(){} //기명 함수 표현식
c(); //ok
d(); //error
기명 함수 표현은 주의할 점이 있다. 외부에서는 함수명으로 함수를 호출할 수 없다. C 함수 내부에서는 c(), d() 호출이 된다. 함수 내부에서 재귀함수를 호출하는 용도로 함수명을 쓸 수 있는 것이다. 하지만, 굳이 d()로 호출하지는 않는다.
let sum = function(a, b) {
return a + b;
};
함수 표현식은 어떨까? 함수를 변수에 할당하는 방식으로, function 키워드를 사용해 변수에 할당할 수 있다. 해당 방식은 호이스팅은 발생하지만 함수가 할당 되기 전 까지는 호출할 수 없다.
- 화살표 함수
함수 표현식을 더 간단하게 선언할 수 있는 방법이 있다.
화살표 함수는 ES6에서 추가된 함수 정의 방법으로, 화살표를 사용해 함수를 정의하는 방법이다.
() => {};
화살표 함수는 익명 함수로만 정의할 수 있어서 함수를 호출하려면 정의된 함수를 변수에 할당 하는 방법인 함수 표현식을 사용해야 한다.
let sum = function(a, b) {
return a + b;
};
//축약 버전
let sum = (a, b) => a + b;
//인수가 없다면 괄호를 비울 수 있다.
let sayHi = () => alert("안녕하세요!");
//매개변수가 1개라면 괄호를 쓰지 않아도 된다.
let double = n => n * 2;
//본문이 여러줄이라면? 중괄호를 사용한다.
let sum = (a, b) => {
let result = a + b;
return result;
};
축약했을 때 return을 생략할 수 있지만, 중괄호를 사용할 땐 return을 생략할 수 없다.
💡 세미콜론
함수 선언문의 경우, 블록이 끝나면 세미콜론을 붙였다. 통상 하나의 명령문이 끝났음을 의미하는데, 보통 블록 단위는 세미콜론을 붙이지 않는다. 하지만 함수 표현식과 화살표 함수는 변수에 할당되는 방식이므로 하나의 명령문에 해당한다.
함수 호이스팅
이전 포스팅에서 변수 호이스팅을 언급했다. 호이스팅은 코드를 선언과 할당으로 분리해 선언부를 자신의 스코프 최상위로 끌어올리는 것을 말한다. 예시를 보자.
console.log(num);
var num = 10;
호이스팅이 일어난다면 아래와 같아진다.
var num;
console.log(num);
num = 10;
//결과는 undefined
var의 경우 선언부가 가장 위로 올라왔다. 여기까지는 이전 포스팅에서 다뤘던 내용이다. 그렇다면, 함수의 경우는 어떻게 될까?
printHello();
function printHello(){
console.log("Hello");
}
이 경우 출력이 어떻게 될까? undefined라고 생각하기 쉽다. 하지만, "Hello"가 정상적으로 출력된다. 호이스팅의 결과를 보자.
function printHello(){
console.log("Hello");
}
printHello();
이처럼 함수 선언문으로 정의된 함수는 호이스팅에서 선언부로 본다. 하지만, 함수 표현식으로 정의된 함수는 조금 다르게 작동한다.
printHello();
var printHello = function printHello(){
console.log("Hello");
}
var printHello;
printHello();
printHello = function printHello(){
console.log("Hello");
}
호이스팅 된 결과다. 앞서 말했듯이 var는 선언부를 가장 위로 올린다고 했다. 그래서 결과는 [TypeError] 발생.
화살표 함수 방식으로 정의해도 결국 함수 표현식처럼 변수에 할당하는 것이므로 함수 표현식과 같은 원리로 호이스팅된다. 단, 함수 표현식이나 화살표 함수를 let이나 const 키워드로 선언했다면 호이스팅 자체가 되지 않는다.
Reference
[1]
자바스크립트 - 동기(Synchronous)? 비동기(asynchronous)?
들어가기 전에, 필자는 자바스크립트를 처음 접하고, 오로지 문법적인 것에만 집중해서 공부를 했었다. 하지만 개발할 때 더 중요한 것은 자바스크립트가 어떻게 동작하는지를 먼저 알고 개발
ljtaek2.tistory.com
[2]
[JavaScript] 자바스크립트 비동기식 처리
동기식 (Synchronous) 먼저 시작된 하나의 작업이 끝날 때까지 다른 작업을 시작하지 않고 기다렸다가 다 끝나면 새로운 작업을 시작하는 방식. 즉, 한 번에 여러 작업을 처리하지 않고 하나만 처리
velog.io
'Programming > JavaScript' 카테고리의 다른 글
[JS] 자바스크립트 실행 컨텍스트, 스코프 체인, environmentRecord/outerEnvironmentReference (0) | 2024.03.26 |
---|---|
[JS] 자바스크립트 조건문, 반복문 (0) | 2024.03.24 |
[JS] 자바스크립트 const, 깊은 복사/얕은 복사 (0) | 2024.03.23 |
[JS] 자바스크립트 변수 var/let, 호이스팅, 블록 스코프/함수 스코프, 불변값/가변값 (0) | 2024.03.23 |
[JS] 자바스크립트 변수의 정의, 선언/할당, 메모리 구조, 리터럴 (0) | 2024.03.23 |