[모던JS] 019. [기본] 함수 (함수선언식, 매개변수, 지역변수, 전역변수, return, 함수명명규칙),
함수 (function)란?
비슷한 동작을 하는 코드를 미리 함수로 선언하고 실행할 수 있게 해주는 기능이다.
앞에서 학습한 alert(message), prompt(message, default), confirm(question) 역시 자바스크립트 내장 함수이다.
함수 선언 (function declaration) (함수 선언식)
함수를 만드는 것을 함수를 선언한다고 한다.
함수를 선언하는 방법은
- 함수 선언식
- 함수 표현식
두 가지 방법이 존재한다.
함수 선언식은 function 함수명() {}와 같은 형태로 함수를 선언하느 것이고 함수 표현식은 let myFunction = function () {}와 같이 변수에 함수를 담는 방법이다.
함수 선언 기본형
function 함수명(매개변수1, 2...) {
// 함수 body (실행할 코드)
}
함수 호출하는 방법 (실행법)
함수명(매개변수);
function showMessage() {
alert('안녕하세요!');
}
showMessage();
showMessage();
함수의 특징
위 예제는 함수를 두 번 호출했기 때문에 alert 창이 두 번 띄워진다.
지역 변수 (local variable)가 되어 외부에서 접근 불가
함수 내에서 선언한 변수는 함수 내에서만 접근할 수 있는 지역 변수가 된다.
function showMessage() {
let message = '안녕하세요!';
alert(message);
}
showMessage(); // 안녕하세요!
alert(message); // 에러 // ReferenceError: message is not defined
외부 변수 (outer variable) 접근/수정 가능 (전역변수)
반대로 함수 내부에서는 함수 외부에서 선언된 외부 변수에 접근할 수 있다.
이렇게 함수 외부에서 선언된 변수는 모든 곳에서 접근 가능하기 때문에 전역 변수(global variable)이라 한다.
let userName = '장원영';
function showMessage() {
let message = 'hello, ' + userName;
alert(message);
}
showMessage(); // hello, 장원영
함수 내부에서 외부 변수에 대해 접근하는 것뿐만 아니라 수정도 할 수 있다.
let userName = '장원영';
function showMessage() {
userName = '안유진'; // userName의 값이 변경됨.
let message = 'hello, ' + userName
}
showMessage(); // hello, 안유진
alert(userName); // 안유진
그런데 함수 외부에서 이미 선언된 변수를 함수 내부에서 값을 수정하는 게 아니라 let, var, const를 통해서 값을 한 번 더 선언한다면 이는 지역변수가 된다. 따라서 함수 외부 변수와 지역 변수가 동시에 같은 이름으로 존재하는 경우, 함수 내부에서는 지역 변수만 접근하게 되고 외부에서는 지역 변수에 접근할 수 없게 된다.
즉, 함수 내부 = 지역 변수 / 함수 외부 = 외부 함수
let userName = '장원영';
function showMessage() {
let userName = '안유진'; // 함수 외부 변수명 과 같은 변수를 지역 변수로 다시 선언
alert('hello, ' + userName) // 위 지역 변수에만 접근 가능
}
showMessage(); // hello, 안유진
alert(userName); // 장원영 // 지역변수에 접근 불가
매개변수(parameter), 인수(argument)
매개변수
매개변수는 인자라고 부르기도 한다. function 함수명(매개변수1, 2...)에 들어가는 것이 매개변수이고, 이를 사용하면 함수 내부로 데이터를 전달할 수 있다.
마치 함수 내부에서 변수를 선언하지 않고, 외부에서 선언해서 함수 내부로 전달하는 것과 같다.
함수 내부에서 변수가 필요할 때 변수에 값을 할당하지 않고 선언만 해두면 외부에서는 지역 변수에 접근이 안 되기 때문에 수정할 수가 없다. 이 때 매개변수를 이용해서 함수 내부로 지역 변수를 선언한 것과 같은 결과를 보일 수 있다.
function showMessage(from, text) {
alert(from + ': ' + text);
}
showMessage('장원영', 'Hello!'); // 장원영 Hello!
showMessage('안유진', 'Hello!'); // 안유진 Hello!
위 예제를 보면 showMessage라는 함수를 선언할 때 매개변수로 from과 text라는 이름을 붙여 임의로 지정했다.
그리고 함수의 body는 메시지창을 띄우도록 했는데 from과 text를 연결시켜 출력하는 코드이다.
그리고 이 함수를 실행할 때는 함수명('from' 매개변수에 전달할 값, 'text' 매개변수에 전달할 값)으로 함수를 실행한다.
그러면 함수를 실행할 때 매개변수에 전달한 값이 함수 내부에도 전달이 되어 함수 내부의 지역 변수 from, text 두 개를 선언한 것과 동일한 효과를 보인다.
인수
그리고 함수를 실행할 때 showMessage('장원영', '안유진';)이라고 전달한 장원영, 안유진이 매개변수로 전달한 값이 되고 이를 인수(arguments)라고 한다.
- 매개변수(parameter) : 함수를 선언할 때 선언하는 지역변수
- 인수(arguments) : 함수를 호출할 때 매개변수에 전달하는 지역변수의 값
기본값
함수의 매개변수 기본값 설정하는 방법 (=)
만약 함수를 호출할 때 인수를 빼 먹으면 함수를 선언할 때 이미 from, text라는 매개 변수를 선언하여 함수 내부에 지역 변수 두 개를 선언한 것과 같기 때문에, 매개 변수를 빼 먹으면 변수만 선언 돼 있고 값은 없는 상태라 undefined가 반환된다.
return만 선언 돼 있고 반환할 값이 없는 경우에도 unefeined를 반환한다.
showMessage('장원영'); // 장원영: undefined
만약 인수를 전달하지 않고도 undefined가 되지 않게 하려면 함수를 선언할 때 매개변수에 기본값을 할당해주면 된다.
function showMessage(from, text = "안녕하세요!") {
alert(from + ": " + text)
}
showMessage('장원영'); // 장원영: 안녕하세요!
// 기본값이 없었으면 장원영: undefined
매개변수에 undefined를 넣어도 기본값이 실행된다.
showMessage('장원영', undefined); // 장원영: 안녕하세요!
기본값으로는 문자열만 넣을 수 있는 게 아니라 함수와 같은 표현식도 넣을 수 있다.
function myFunction(text = yourFunction()) {
// 함수 body
}
// 매개변수 text에 값이 전달되지 않을 때 기본값으로 넣은 yourFunction()이라는 함수가 호출된다.
// yourFunction()함수의 결과로 반환된 값이 text에 전달된다.
구식 자바스크립트에서 기본값 설정하는 방법
가끔은 이런 방법이 적절할 때도 있다.
// undefined인지 확인하는 방법
function showMessage(from, text) {
if (text === undefined) {
text = '값이 없습니다.';
}
alert(from + text);
}
// || 논리 연산자를 사용하는 방법
function showMessage(from, text) {
text = text || '값이 없습니다.';
// 함수 body
}
// || 논리 연산자는 truthy한 값을 반환한다. 즉 좌항인 text가 undefined라면 우항의 '값이 없습니다.'가
// 텍스트가 있어서 truthy한 값이기에 이 값을 text라는 지역변수에 할당한다.
모던 자바스크립트 ?? nullish 병합 연산자를 사용하여 기본값 설정하는 방법
?? nullish 병합 연산자는 a ?? b 형태로 쓰고, 좌항인 a가 null이나 undefined가 아니라면 무조건 좌항을 반환하고, 그 외의 경우는 무조건 우항을 반환한다. 즉 둘 다 null이나 undefined여도 우항을 반환한다.
function showCount(count) {
alert(count ?? "값이 없습니다.");
}
showCount(0); // 0
showCount(null); // 값이 없습니다.
showCount(); // 값이 없습니다.
반환 값 (return)
return 기본형
함수 내부에서 return을 사용하면 함수를 호출한 곳으로 함수 내부에서 연산한 값을 전달할 수 있다.
function sum(a, b) {}
let result = sum(1, 2);
console.log(result); // undefined
그런데 여기에 return을 함수 body에 사용하면 함수를 호출한 곳, 즉 인수를 전달한 곳에 return에서 지정한 내용대로 값을 반환하게 된다.
function sum(a, b) {
return a + b;
}
let result = sum(1, 2);
console.log(result); // 3
a와 b를 더한 값을 함수를 호출한 sum(1, 2);에 3이라는 값을 전달한다.
return 사용 규칙
- 함수 내부 어디에다 사용해도 상관없다.
- 함수 하나에 여러 개의 return이 올 수도 있다.
function checkAge(age) {
if (age >= 18) {
return true;
} else {
return confirm('보호자의 동의를 받으셨나요?');
}
}
let age = prompt('나이를 알려주세요.', 18);
if ( checkAge(age) ) {
alert( '접속 허용' );
} else {
alert( '접속 차단' );
}
코드 해석
- function으로 age라는 매개변수를 받는 함수 checkAge를 선언한다.
- prompt 창을 띄워 사용자에게 나이를 입력받는다. 기본값은 18이다.
- prompt에서 입력받은 값은 age라는 변수에 할당된다.
- 다시 함수로 와서 내용을 살펴보면, prompt창을 통해 사용자에게 age 값을 입력받고 이 age를 if문을 통해 18과 같거나 큰 지 확인한다.
- 참이라면 true라는 불린값을 함수를 호출한 곳에 전달하고, 거짓이라면 confirm창을 띄워 보호자의 동의 여부를 묻는 확인 창을 띄운다.
- 마지막 코드에서 함수가 호출되었는데 if문의 조건식 내부에서 호출됐다. checkAge(age)로 매개변수 age의 값은 이미 사용자가 입력해서 age라는 변수에 담았기 때문에 매개변수의 원형을 그대로 입력해서 호출한 것이다.
- 사용자가 입력한 age에 따라 함수 내부에서 평가한 age => 18에 따라 true를 반환했다면 if문의 alert('접속 허용')이 실행되고 false라면 confirm 함수가 반환되어 확인창 먼저 뜬 후 alert('접속 차단')이 뜬다.
- 값을 입력하지 않으면 undefined이기 때문에 역시 falsy한 값으로 진행된다.
- return만 명시하는 것도 가능하다. 이런 경우 함수는 즉시 종료된다.
function showMovie(age) {
if ( !checkAge(age) ) {
return;
}
alert( '영화 상영' );
}
위의 코드에서는 매개변수 age의 값을 외부에서 전달받은 뒤이 값이 falsy하다면 함수가 즉시 종료된다. 즉 아래에 작성된 alert 코드는 실행되지 않는다.
- return을 다른 줄에 작성하면 안 된다.
// 올바르지 못한 예시
return
(some + ...........................................);
// 올바른 예시
return (some + ..................................);
코드가 길다고 다른 라인으로 작성하면 위 코드에서는 return과 아래 반환할 값은 별도의 코드로 인식한다. return에 세미콜론을 찍지 않았어도 자바스크립트에서 자동으로 세미콜론을 넣어서 코드를 종료시키기 때문이다.
만약 굳이 라인을 나눠야겠다면 반환할 표현식의 시작을 한 글자라도 return문의 라인에서 시작하도록 하면 된다.
return (
some + ...................................
);
함수 명명 규칙
함수는 어떤 동작을 하는 코드이다. 따라서 함수의 이름은 대개 동사이다. 그리고 몇 가지 지키면 좋은 규칙이 있다. 의무는 아니지만 가독성을 위해 거의 필수와 다름없다.
- camelCase 명명 규칙 준수
- 명확하고 간결하게 어떤 동작을 하는지를 동사형으로 내포
- 어떤 동작을 하는지 자주 사용하는 접두어를 사용하여 설명
접두어 | 설명 | 예시 |
---|---|---|
get | 어떤 값을 반환하는 함수 | getName, getTotalPrice |
set | 어떤 값을 설정하거나 업데이트하는 함수 | setName, setQuantity |
calc | 계산을 수행하는 함수 | calcTotalPrice, calcTax |
creat | 무언가를 생성하는 함수 | creatForm |
check | 무언가를 확인하고 불린값으로 반환하는 함수 | checkPermission |
validate | 유효성을 검증하는 함수 | validateEmail, validatePassword |
format | 데이터를 특정 함수로 변환하는 함수 | fromatDat, formatCurrecy |
show | 화면에 정보를 표시하는 함수 | showErrorMessage, showSuccessMessage |
handle | 이벤트 핸들링이나 상태 관리를 위한 함수 | handleClick, handleInputChange |
fetch | 데이터를 가져오거나 요청하는 함수 | fetchUserData, fetchProducts |
toggle | 상태를 전환하거나 토글하는 함수 | toggleModal, toggleMenu |
remove | 요소나 항목을 삭제하는 함수 | removeItem, removeUser |
load | 데이터나 자원을 로드하는 함수 | loadData, loadImage |
initialize | 초기화 작업을 수행하는 함수 | initializeApp, initializeSettings |
submit | 양식을 제출하거나 데이터를 전송하는 함수 | submitForm, submitData |
hide | 요소를 숨기거나 가리는 함수 | hideMenu, hideModal |
transform | 데이터나 요소를 변형하거나 변경하는 함수 | transformData, transformElement |
render | 화면이나 요소를 렌더링하는 함수 | renderUserList, renderDashboard |
- 함수 하나는 동작 하나만 담당해야 한다.
개발자들이 자주 하는 실수로, 함수 하나에 여러 동작을 넣는 경우가 있다. 예를들어 getAge 함수는 나이를 얻어오는 함수가 될 것이므로 함수 body에 alert창을 띄우는 동작을 넣는 것은 좋지 않다. 별도로 분리하는 것이 좋다.
creatForm 함수는 form을 만드는 함수이므로, 이 동작만 하는 것이 좋고 form을 HTML 문서에 삽입하는 동작은 이 함수에 포함되지 않는 것이 좋다.
checkPermission 함수는 승인 여부를 확인하고 그 결과를 반환하는 함수이므로 승인 여부를 보여주는 메시지를 출력하는 alert 함수를 넣는 것은 좋지 않다.
- 명명규칙 예외사항
자바스크립트 프레임워크에서는 이 명명규칙을 어긴다. 따라서 이런 프레임워크를 사용할 때는 예외가 된다. 예를들어 jQuery에서는 $를 사용하고, Lodash 라이브러리에서는 _를 사용한다.
- 함수가 길어지면 쪼갠다. 함수는 곧 주석이다.
코드를 작성할 때 주석을 다는 이유는 가독성을 높이기 위함이다. 그런데 함수의 이름에서 이미 어떤 동작인지 그 의미를 내포하고 있기 때문에 함수명만 잘 지어도 주석을 단 것과 다름없다.
그런데 명명규칙과는 다르게 위의 예시처럼 creatForm 함수인데 이 안에 다른 동작이 들어가있다면 이 주석은 무의미하다.
따라서 함수는 최대한 간결한 것이 좋으며 함수가 길어질 경우 여러개로 나누는 것이 가독성에서 유리하다.
function checkAge(age) {
if (age >= 18) {
alert('접속 허용');
} else {
if (confirm('보호자의 동의를 받으셨나요?')) {
alert('접속 허용');
} else {
alert('접속 차단');
}
}
}
let age = prompt('나이를 알려주세요', 18);
checkAge(age);
위의 예제에는 checkAge함수 하나에 (1) 사용자의 나이를 입력받고 (2) 나이가 18세 이상인지 확인하고 (3) 18세 미만이라면 보호자 동의 여부를 묻는 확인창을 띄우고 (4) 결과에 따라 접속 허용 여부를 보여주는 네 가지의 동작이 들어가있다.
함수 명명 규칙에도 위배되지만 코드 가독성이 떨어지기 때문에 이를 여러개로 쪼개면 아래와 같다.
function checkAge(age) {
return age >= 18;
}
function getGuardianApproval() {
return confirm('보호자의 동의를 받으셨나요?');
}
function allowAccess() {
alert('접속 허용');
}
function blockAccess() {
alert('접속 차단');
}
let age = prompt('나이를 알려주세요', 18);
if (checkAge(age)) {
allowAccess();
} else {
if (getGuardianApproval()) {
allowAccess();
} else {
blockAccess();
}
}
사실 위 코드는 극단적으로 모든 동작을 분리한 코드이다. 코드가 불필요하게 길어진 것 같지만 함수의 이름만 보더라도 어떤 동작이 있을지 예상이 가능하기 때문에 오히려 가독성은 더 좋아졌다. 이렇게 함수 이름만 보고도 동작을 예측할 수 있는 함수를 자기 설명적(self-describing)코드라고 한다.
연습문제
- 아래 함수는 매개변수 age가 18보다 큰 경우 true를 반환한다. 그 이외의 경우는 컨펌 대화상자를 통해 사용자에게 질문한 후 해당 결과를 반환한다.
function checkAge(age) {
if (age > 18) {
return true;
} else {
// ...
return confirm('보호자의 동의를 받으셨나요?');
}
}
위 코드에서 아래와 같이 else 문을 삭제해도 기존 코드와 동일하게 작동할 것 같은지 예측해보시오. 아니라면 어떤 변화가 있을 것인지 예측해보시오.
function checkAge(age) {
if (age > 18) {
return true;
}
// ...
return confirm('보호자의 동의를 받으셨나요?');
}
- 아래 함수는 매개변수 age가 18보다 큰 경우 true를 반환한다. 그 이외의 경우에는 컨펌 대화상자를 통해 사용자에게 질문한 후 해당 결과를 반환한다.
function checkAge(age) {
if (age > 18) {
return true;
} else {
return confirm('보호자의 동의를 받으셨나요?');
}
}
위의 함수를 if문을 작성하지 않고 동일한 동작을 하도록 함수를 작성해보시오. 단 아래의 조건을 충족하도록 해서 각각의 코드를 작성하시오.
- 물음표 연산자 ? 를 사용하여 본문 작성
- OR 연산자 ||를 사용하여 본문 작성
- min(a, b) 함수 만들기
a와 b 중 작은 값을 반환해주는 함수 min(a, b)를 만들어보시오.
만든 함수는 아래와 같이 동작해야 합니다.
min(2, 5) == 2
min(3, -1) == -1
min(1, 1) == 1
- x의 n제곱을 반환해주는 함수, pow(x,n)를 만들어보시오. x의 n 제곱은 x를 n번 곱해서 만들 수 있습니다.
pow(3, 2) = 3 * 3 = 9
pow(3, 3) = 3 * 3 * 3 = 27
pow(1, 100) = 1 * 1 * ...* 1 = 1
프롬프트 대화상자를 띄워 사용자로부터 x와 n을 입력받고 pow(x,n)의 반환 값을 보여주는 코드를 작성해보시오.
해답
- 동일하게 동작한다.
else문을 뺀 아래 코드를 보면 if 조건식에서 age가 18 이상인지 확인하고 참이라면 {} 중괄호 내부의 함수 body를 실행하게 되는데 true를 반환하고 바로 종료시킬 것이고, 조건식이 falsy하다면 함수 body를 실행하지 않고 다음 코드를 실행시키기에 confirm 대화상자를 띄운다. 즉 동일하게 동작한다. -
function checkAge(age) { return (age>18) ? true : confirm('보호자의 동의를 받으셨나요?') };
checkAge(20);
return을 넣는 이유는 함수 내부에서 조건식이 true면 true를 반환하고, false면 confirm 대화상자를 띄우는 함수를 실행하도록 한 함수 내부의 코드를 외부로 전달하기 위해서다. 즉 함수 body의 내용을 함수를 호출한 곳으로 전달하기 위해서이고, return을 넣지 않으면 함수 내부에서만 해당 코드가 실행되기 때문에 외부에서 값을 전달받을 수 없다.
? 물음표는 (condition) ? a : b 형태로 작성하는데 condition이 true면 a를 반환하고 그 외에는 b를 반환한다.
```javascript
function checkAge(age) {
return (age > 18) || confirm('보호자의 동의를 받으셨나요?');
}
|| OR 논리 연산자는 피연산자가 2개 일 때와 3개 이상일 때의 알고리즘이 다르다. 2개일 때는 하나라도 truthy한 값이면 true 불린값을 반환하고, 세 개 이상이면 왼쪽부터 차례대로 비교하다 먼저 나오는 truthy한 값의 원래의 값을 반환하고, 모두 falsy하다면 가장 마지막 피연산자의 원래의 값을 반환한다.
즉 위의 코드를 보면 함수 body에서 (age > 18)이 truthy한 값인지 검사하는데 age가 18이상이라 truthy한 값이 되면 true 불린값을 return하게 되고, 18 미만으로 인수가 전달되어 falsy한 값이 되면 우항을 검사하는데 우항은 confirm 내장'함수'이므로, 함수는 객체이고 객체는 truthy한 값이 되어 우항을 return하게 된다.
물음표 연산자 ? 를 사용한 해답
function min(a, b) {
return (a < b) ? a : b;
}
if문을 사용한 해답
function min(a, b) {
if ( a < b ) {
return a;
} else {
return b;
}
}
function pow(x, n) {
let result = x;
for (let i = 1; i < n; i++) {
result *= x;
}
return result;
}
let x = prompt("x?", '');
let n = prompt("n?", '');
if (n < 1) {
alert(`${n}은 양의 정수이어야 합니다.`);
} else {
alert( pow(x, n) );
}
'Programing > JavaScript' 카테고리의 다른 글
[모던JS] 021. [기본] 화살표 함수 기본 (0) | 2024.03.30 |
---|---|
[모던JS] 020. [기본] 함수 표현식(콜백 함수, 익명 함수) (0) | 2024.03.30 |
[모던JS] 018. [기본] switch문 (0) | 2024.03.28 |
[모던JS] 017. [기본] while과 for 반복문 (0) | 2024.03.28 |
[모던JS] 016. [기본] nullish 병합 연산자 (0) | 2024.03.28 |
댓글