자바스크립트 기본 - 함수

devheyrin·2022년 4월 20일
0

modern javascript

목록 보기
15/26

사용자가 로그인이나 로그아웃을 했을 때 안내 메시지를 보여주는 동작처럼 유사한 동작을 하는 코드가 여러 곳에서 필요할 때가 많다.

함수를 이용하면 유사한 동작을 하는 코드를 중복해 작성하지 않고도 여러 번 호출할 수 있다.

함수를 직접 만드는 방법을 알아보자!

함수 선언

함수선언방식(함수선언문) 으로 함수를 만들 수 있다. 다음과 같이 작성한다.

function showMessage() {
  alert( '안녕하세요!' );
}

function 키워드, 함수이름, 괄호로 둘러싼 매개변수를 차례로 써주면 함수를 선언할 수 있다. 매개변수가 여러 개 있다면 각각을 콤마로 구분해준다. 이어서 함수 본문을 중괄호로 감싸 준다.

function name(param1, param2) {
  ...함수 본문...
}

새로 정의된 함수는 이름 옆에 괄호를 붙여 호출할 수 있다.

function showMessage() {
  alert( '안녕하세요!' );
}
showMessage();

지역 변수

함수 내에서 선언한 변수인 지역변수는 함수 안에서만 접근할 수 있다.

function showMessage() {
  let message = "안녕하세요!"; // 지역 변수

  alert( message );
}

showMessage(); // 안녕하세요!

alert( message ); 
// ReferenceError: message is not defined 

message 는 함수 내에서 선언한 지역변수이기 때문에 함수 바깥에서는 접근할 수 없다.

외부 변수

함수 내부에서 함수 외부 변수에 접근할 수 있다.

let userName = 'John';

function showMessage() {
  let message = 'Hello, ' + userName;
  alert(message);
}

showMessage(); // Hello, John

함수를 이용해 외부 변수를 수정할수도 있다.

let userName = 'John';

function showMessage() {
  userName = "Bob"; // (1) 외부 변수를 수정함

  let message = 'Hello, ' + userName;
  alert(message);
}

alert( userName ); // 함수 호출 전이므로 John 이 출력됨

showMessage();

alert( userName ); // 함수에 의해 Bob 으로 값이 바뀜

외부 변수는 지역 변수가 없는 경우에만 사용할 수 있다.

함수 내부에 외부 변수와 같은 이름의 지역변수가 선언되었다면, 내부 변수가 외부 변수를 가린다. (외부 변수의 값은 수정되지 않는다.)

let userName = 'John';

function showMessage() {
  let userName = "Bob"; // 외부 변수와 같은 이름의 지역변수 선언 

  let message = 'Hello, ' + userName; // Bob
  alert(message);
}

alert( userName ); // 외부 변수인 John 출력 

showMessage();

alert( userName ); // John 출력. 함수는 외부 변수에 접근하지 않으므로 값이 변경되지 않음 
💡 전역 변수
함수 외부에 선언된 변수는 전역변수라고 부른다. 전역 변수는 같은 이름을 가진 지역 변수에 의해 가려지지만 않는다면 모든 함수에서 접근할 수 있다.
변수는 연관되는 함수 내부에 선언하고, 전역 변수는 되도록 사용하지 않는 것이 좋다. 단, 프로젝트 전반에서 사용되는 데이터는 전역 변수로 저장하는 것이 유용한 경우도 있다.

매개변수

매개변수를 이용하면 임의의 데이터를 함수 안에 전달할 수 있다. 매개변수는 인수(parameter) 라고 부르기도 한다.

함수를 호출하면, 함수에 전달된 인자는 지역변수 from 과 text 에 복사된다. 그 후 함수는 지역변수에 복사된 값을 사용한다.

전역변수 from 이 있고, 이 변수를 함수에 전달하는 경우 함수가 from 을 변경하지만, 변경 사항은 외부 변수 from 에 반영되지 않는다. 함수는 언제나 복사된 값을 사용하기 때문이다.

function showMessage(from, text) {

  from = '*' + from + '*'; // "from"을 변경한다.

  alert( from + ': ' + text );
}

let from = "Ann";

showMessage(from, "Hello"); // *Ann*: Hello

// 함수는 복사된 값을 사용하기 때문에 바깥의 "from"은 값이 변경되지 않음 
alert( from ); // Ann

기본값

매개변수에 값을 전달하지 않으면 그 값은 undefined 가 된다. 위에서 정의함 함수는 매개변수가 2개지만, 인수를 하나만 넣어서 호출할 수도 있다.

function showMessage(from, text) {
  alert( from + ": " + text );
}

showMessage("Ann"); // Ann: undefined

매개변수에 값을 전달하지 않아도 undefined 를 출력되지 않게 하려면 기본값을 설정해주어야 한다.

function showMessage(from, text = "no text given") {
  alert( from + ": " + text );
}

showMessage("Ann"); // Ann: no text given

기본값으로는 함수를 넣어줄 수 도 있다.

function showMessage(from, text = anotherFunction()) {
  // anotherFunction()은 text값이 없을 때만 호출됨
  // anotherFunction()의 반환 값이 text의 값이 됨
}

매개변수 기본값 설정 방법

함수 선언부에서 매개변수 기본값을 설정하기보다 함수 실행 도중 기본값을 설정하는 것이 맞는 경우도 있다.

이러한 경우 매개변수를 undefined 와 비교하여 매개변수 생략 여부를 검사한다.

function showMessage(text) {
  if (text === undefined) {
    text = '빈 문자열';
  }

  alert(text);
}

showMessage(); // 빈 문자열

논리 연산자 || 를 사용해도 동일하게 작동한다.

function showMessage(text) {
  text = text || '빈 문자열';
  ...
}

모던 자바스크립트 엔진이 지원하는 nullish 병합 연산자 ?? 를 사용하면 0처럼 falsy 로 평가되는 값들을 일반 값처럼 처리할 수 있다.

// 매개변수 'count'가 null, undefined 이면 'unknown'을 출력해주는 함수
function showCount(count) {
  alert(count ?? "unknown");
}

showCount(0); // 0
showCount(null); // unknown
showCount(); // unknown

반환 값

함수를 호출했을 때 함수를 호출한 그곳에 특정 값을 반환하게 할 수 있다. 이 특정 값을 반환 값이라고 부른다.

반환 값은 지시자 return 을 사용해 표현한다. return 은 함수 내 어디서든 사용할 수 있고, return 을 만나면 함수 실행이 즉시 중단되며 호출한 곳에 값을 반환한다.

함수 하나에 여러 개의 return 문이 올 수도 있다.

function checkAge(age) {
  if (age >= 18) {
    return true;
  } else {
    return confirm('보호자의 동의를 받으셨나요?');
  }
}

let age = prompt('나이를 알려주세요', 18);

if ( checkAge(age) ) {
  alert( '접속 허용' );
} else {
  alert( '접속 차단' );
}

지시자 return 만 명시하는 것도 가능하며, 이런 경우 함수가 즉시 종료된다.

function showMovie(age) {
  if ( !checkAge(age) ) {
    return;
  }

  alert( "영화 상영" ); // (*)
  // ...
}

위 예시에서 checkAge()가 false 를 반환하면 함수가 종료되므로 alert() 가 실행되지 않는다.

💡 return 문이 없거나 return 으로 종료되는 함수
return 문이 없거나 반환 값 없이 return 지시자만 있는 함수는 undefined 를 반환한다. 💡 return 과 반환값 사이에 줄을 삽입하면 안 된다!

반환값으로 여러 줄을 작성하고 싶다면 표현식이 return 지시자가 있는 곳에서 시작하도록 해야 한다.

return (
  some + long + expression
  + or +
  whatever * f(a) + f(b)
  )

함수 이름짓기

함수란 어떤 동작을 수행하기 위한 코드를 모아 놓은 것이다. 따라서 함수의 이름은 대개 동사이다.

함수 이름은 가능한 간결하고 명확해야 하고, 함수의 동작을 설명할 수 있어야 한다.

코드를 읽는 사람이 함수 이름만 보고도 함수의 기능을 짐작할 수 있도록 작성해야 한다.

함수의 동작을 축약해서 설명해주는 동사를 접두어로 붙여 함수 이름을 만드는 것이 관습이지만, 팀 내에서 합의된 접두어만 사용해야 한다.

다음은 대체로 사용하는 접두어들이다.

  • show - 무언가를 보여주는 함수
  • get - 값을 반환하는 함수
  • calc - 무언가를 계산하는 함수
  • create - 무언가를 생성하는 함수
  • check - 무언가를 확인하고 불린값을 반환하는 함수
showMessage(..)     // 메시지를 보여줌
getAge(..)          // 나이를 나타내는 값을 얻고 그 값을 반환함
calcSum(..)         // 합계를 계산하고 그 결과를 반환함
createForm(..)      // form을 생성하고 만들어진 form을 반환함
checkPermission(..) // 승인 여부를 확인하고 true나 false를 반환함
💡 함수는 동작 하나만 담당해야 한다.
함수는 함수 이름에 언급되어 있는 동작을 정확히 수행해야 하며, 그 이외의 동작은 수행하면 안 된다. 독립적인 두 개의 동작은 독립된 함수 두 개에서 나누어 수행할 수 있게 만들어야 한다. 한 장소에서 두 동작을 동시에 필요로 하는 경우, 제 3의 함수를 만들어 그곳에서 두 함수를 호출하는 것이 좋다.
- getAge 함수는 나이를 가져오는 동작만 수행해야 한다. alert() 창에 나이를 출력하는 동작은 함수에 들어가지 않는 것이 좋다. getAge 와 showAge 함수를 각각 만들고, showAge 함수에서 getAge 를 호출해 결과를 alert 로 보여주도록 만드는 것이 좋다.
- createForm 함수는 폼을 만들고 이를 반환하는 동작만 해야 한다. form 을 문서에 추가하는 동작이 들어가지 않는 것이 좋다.
- checkPermission 함수는 승인 여부를 확인하고 그 결과를 반환하는 동작만 해야 한다. 승인 여부를 보여주는 메시지를 띄우는 동작이 들어가지 않는 것이 좋다.

함수 == 주석

함수는 간결하고, 한 가지 기능만 수행할 수 있게 만들어야 한다. 함수가 길어지면 함수를 잘게 쪼갤 때가 되었다는 것으로 받아들여야 한다. 함수를 쪼개는 것은 쉬운 작업이 아니지만, 분리해 작성하면 많은 장점이 있다. 장점으로는 테스트와 디버깅이 쉬워지고, 함수 그 자체로 주석의 역할까지 한다는 점이 있다!

다음의 두 코드를 비교해 보면 어떤 동작을 하는지 알아차리기 쉬운 쪽이 어디인지 명확하게 알 수 있다.

일반적으로 작성한 코드 (showPrimes 함수 내에 소수 판별 기능을 포함)

function showPrimes(n) {
  nextPrime: for (let i = 2; i < n; i++) {

    for (let j = 2; j < i; j++) {
      if (i % j == 0) continue nextPrime;
    }

    alert( i ); // 소수
  }
}

소수 판별 기능을 분리해서 작성한 코드

function showPrimes(n) {

  for (let i = 2; i < n; i++) {
    if (!isPrime(i)) continue;

    alert(i);  // a prime
  }
}

function isPrime(n) {
  for (let i = 2; i < n; i++) {
    if ( n % i == 0) return false;
  }
  return true;
}

이렇게 이름만 보고도 어떤 동작을 하는지 알 수 있는 코드를 자기 설명적 코드라고 부른다.

profile
개발자 헤이린 🔜 프로덕트 매니저로 나아가는 중!

0개의 댓글