- 2024년 1월 25일 작성하여 벨로그에서 이전한 글.
프로젝트 소스를 보다보면 함수 형태가 다양해서 차이점을 좀 더 자세히 알아보기로 했다.
함수, 꼭 써야하나?
함수를 사용하는 이유중에 하나는 재사용성이 좋기 때문이다. 같은 일을 맡아 진행하는데 여러번 코드를 적을 필요가 없어진다. 그 코드가 수정할 일이 생길 때 그 함수만 수정하면 되기 때문이다.
필자는 사실 초반에 필요성을 못느끼고 귀찮다는 이유로 코드를 복붙하는 경우가 많았는데, 시간이 얼마 지나지 않아 과거의 나를 무척이나 원망했다^^..
함수선언
함수 선언, 함수 정의, 기명함수이라고도 한다.
// 선언
function multiply(x, y) {
return x * y;
} // No need for semicolon here
multiply는 함수 이름,
(x, y)안의 x,y는 매개변수(매개변수는 함수 내의 지역변수처럼 취급된다.),
{}안에는 정의할 내용,
return은 함수가 반환하는 값을 지정한다. return값이 없으면 undefined가 리턴된다.
함수는 정의했다고 실행되지 않는다. 호출해야만 실행된다.(중요)
매개변수
함수의 매개변수는 재할당해도 외부의 변수값에는 영향을 미치지 않지만, 객체의 속성값에는 영향을 미친다.
function chgCover(_book) { //_book이 매개변수
_book.cover = "딸기 생크림 케이크 사진";
_book = null;
}
const book = {
cover: "소금이 뿌려진 식빵 사진",
contents: ["식빵 굽기", "케이크 만들기", "쿠키 굽기"],
title: "베이킹을 시작해볼까",
};
console.log(book.cover); // 소금이 뿌려진 식빵 사진
// 함수 호출
chgCover(book);
console.log(book);
console.log(book.cover); // 딸기 생크림 케이크 사진
매개변수가 배열이라면, 배열의 각 index의 값을 바꾸는 것도 가능하다.
const arr = [1,2,3,4,5];
function chgArr(_arr) {
_arr[0] = 10;
}
chgArr(arr);
console.log(arr); // [10, 2, 3, 4, 5]
만약 함수의 매개변수가 정의된 것보다 적게 들어간다면, 나머지의 매개변수는 undefined 형태다.
- arguments 객체 활용
: 함수 내부에서 arguments 객체를 활용할 수 있다. 배열처럼 length를 확인할 수 있고, arguments[0]처럼 직접적으로 매개변수에 접근할 수도 있다.
호이스팅
console.log(sum(5,5)); // 10
function sum(x,y) {
return x+y;
}
함수가 콘솔을 찍기전에 선언이 되어있어도 에러가 발생하지 않는다.
JavaScript 인터프리터가 전체 함수 선언을 현재 스코프의 최상단으로 끌어올리기 때문(호이스팅)이다. (변수에 담는 함수 선언식은 호이스팅되지 않는다.)
함수 표현
함수표현식은 익명함수라고도 하는데, 함수가 이름을 가질 필요는 없음을 의미한다. 변수에 함수를 할당하는 방식이다.
함수 표현식, 왜 쓰는걸까?
함수표현식은 보통 새로 다른 함수의 일부로 이용될 때 유리하다. 다른 함수에 대한 할당이나 호출 시 보통 사용한다. 보통 한번만 호출되는 함수에 특히 적합하다.
변수에 할당되기 전까지는 못쓴다.
따라서 표현식을 사용하려면 변수가 먼저 선언된 후에 사용해야한다.
// 함수표현식
const sum1 = function (x, y) {
return x + y;
};
// 함수표현식에 함수 이름을 부여할 수도 있다.
const sum2 = function fnSum(x, y) {
return x + y;
};
표현식에 경우 고유한 이름을 가져도, 호출 시 변수명으로 호출해야 한다.
함수표현식에 고유명은 함수 내부에서만 참조 가능하다. (재귀호출)
객체의 메서드
const calc = {
sum(a, b) {
this.s_result = a+b;
},
minus: function(a, b) {
this.m_result = a-b;
}
};
메서드는 객체의 속성으로 저장된 함수이며, 객체이기 때문에 this를 통해 calc자체를 참조할 수 있다.
// 접근 방식 1. .활용
calc.sum(1,2);
// 접근 방식 2. [] 활용
calc["sum"](1,2);
함수안에 함수(중첩함수)
함수는 사실 객체다.
function outer() {
const outerScope = "밖";
inner();
function inner() {
const innerScope = "안";
console.log("in", outerScope);
console.log("in", innerScope);
}
console.log("in", innerScope);
}
자바스크립트의 함수는 중첩이 가능하다.
여기서 inner함수는 outer함수의 변수에 접근이 가능한데, outer함수가 inner함수의 변수에 접근은 불가능하다.
클로저
함수 객체와 함수의 변수가 해석되는 유효범위를 클로저라고 한다.
함수는 호출시점에서의 변수 유효범위가 아닌, 함수가 정의된 시점의 변수 유효범위를 사용한다.
function outer() {
let scope = "local";
function inner() {
return scope;
}
return inner;
}
outer()(); // "local"
아래의 경우에는 호출 때마다 리턴 값이 다르다. 전역변수 대신 많이 사용하는 방법이다.
let call = function() {
let now = "a";
return function() {
now = now == "a" ? "b" : "a";
return now;
}
}();
console.log(call()); // b
console.log(call()); // a
정리하며
나는 함수를 알고 있다고 생각했는데, 내가 안다고 말하는 것은 결코 아는 것이 아니었음을, 표면에 지나지 않음을 많이 느낀다.
시작은 분명 그랬으나, 찾아보면 찾아볼수록 함수 자체는 어마어마한 양을 담고 있구나를 많이 느꼈다.
다음에 작업할 때는 무조건 기명함수를 사용할게 아니라, 일회성에 의한 부분은 익명함수를 활용해보도록 해야겠다.
기존에 전역변수를 활용한 방법만 이용했는데, 클로저 특성을 활용한 기능구현을다좀 더 구체적으로 알아봐도 좋을 것 같다.
본래는 생성자함수와 화살표함수 관련해서도 같이 기록하려고 했으나, 클로저를 이해하는데 있어 많은 어려움을 겪었다....
지금도 완벽하게 이해한게 맞나..?싶기도 하다.
그래서 생성자함수와 화살표함수는 (다음기회에) 포스팅 해야겠다.
참고자료
자바스크립트 완벽가이드(저자: 데이비드 플래너건)
https://poiemaweb.com/js-closure
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Functions
https://likedev.tistory.com/entry/javascript-%EC%9D%B5%EB%AA%85%ED%95%A8%EC%88%98
https://dev-note-97.tistory.com/273
https://hhyemi.github.io/2021/06/09/arrow.html
https://chaeyoung2.tistory.com/76#google_vignette
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Function
https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Functions#function_hoisting
'프로그래밍 > Javascript' 카테고리의 다른 글
[모던 자바스크립트 Deep Dive] 브라우저 렌더링 과정 (0) | 2024.08.27 |
---|---|
[JS] 변수: var, let, const (0) | 2024.05.28 |
[Javascript] Function() 생성자, 화살표 함수 (0) | 2024.04.17 |
[Javascript] 비동기 처리: Promise객체, fetch, async / await (0) | 2024.04.09 |