ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 혼자 공부하는 자바스크립트 5장 - 함수
    자바스크립트/혼자 공부하는 자바스크립트 2021. 7. 31. 00:49

    함수

    • 함수 호출
      • 함수를 사용한다.
    • 매개변수
      • 함수를 호출할 때 괄호 내부에 넣는 자료
    • 리턴값
      • 함수를 호출해서 최종적으로 나오는 결과

     

    익명함수

    함수는 코드의 집합을 나타내는 자료형이다. 중괄호 내부에 코드를 넣어 사용하기 때문이다.

    function () {}

    함수를 사용하면 좋은 점:

    • 반복되는 코드를 한 번만 정의해놓고 필요할 때마다 호출하므로 반복 작업을 피할 수 있다.
    • 긴 프로그램을 기능별로 나눠 여러 함수로 나누어 작성하면 모듈화로 전체 코드의 가독성이 좋아진다.
    • 기능별(함수별)로 수정이 가능하므로 유지보수가 쉽다.
    const 함수 = function () {
      console.log("함수 내부의 코드입니다 ... 1");
      console.log("함수 내부의 코드입니다 ... 2");
      console.log("함수 내부의 코드입니다 ... 3");
      console.log("");
    }
    
    함수();
    // 함수 내부의 코드입니다 ... 1
    // 함수 내부의 코드입니다 ... 2
    // 함수 내부의 코드입니다 ... 3
    // 
    
    함수();
    // 함수 내부의 코드입니다 ... 1
    // 함수 내부의 코드입니다 ... 2
    // 함수 내부의 코드입니다 ... 3
    // 
    
    console.log(typeof 함수);        // function
    console.log(함수);
    // f() {
    //   console.log("함수 내부의 코드입니다 ... 1");
    //   console.log("함수 내부의 코드입니다 ... 2");
    //   console.log("함수 내부의 코드입니다 ... 3");
    //   console.log("");
    // }

    함수의 자료형은 function이며 위의 예시처럼 이름이 붙어있지 않은 함수를 익명함수라고 한다.

     

    선언적 함수

    일반적으로는 익명함수보다 이름이 있는 선언적 함수를 많이 사용한다.

    function 함수() {
    
    }

    이 함수는 조금의 차이는 있지만 다음 코드와 같은 기능을 수행한다:

    let 함수 = () { };
    function 함수 () {
      console.log("함수 내부의 코드입니다 ... 1");
      console.log("함수 내부의 코드입니다 ... 2");
      console.log("함수 내부의 코드입니다 ... 3");
      console.log("");
    }
    
    함수();
    // 함수 내부의 코드입니다 ... 1
    // 함수 내부의 코드입니다 ... 2
    // 함수 내부의 코드입니다 ... 3
    // 
    
    함수();
    // 함수 내부의 코드입니다 ... 1
    // 함수 내부의 코드입니다 ... 2
    // 함수 내부의 코드입니다 ... 3
    // 
    
    console.log(typeof 함수);        // function
    console.log(함수);
    // f 함수 () {
    //   console.log("함수 내부의 코드입니다 ... 1");
    //   console.log("함수 내부의 코드입니다 ... 2");
    //   console.log("함수 내부의 코드입니다 ... 3");
    //   console.log("");
    // }

    익명함수의 예시와는 다르게 함수에 이름이 붙어있는 것을 알 수 있다.

     

    매개변수와 리턴값

    함수를 호출할 때 괄호 안에 적는 것을 매개변수라고 하고, 함수의 최종 결과를 리턴값이라고 한다.

    function 함수(매개변수, 매개변수, 매개변수) {
      문장
      문장
      return 리턴값
    }

    모든 함수에 매개변수와 리턴값을 사용하는 것이 아니라, 필요한 경우에만 매개변수와 리턴값을 사용한다. 가령 위의 선언적 함수는 매개변수와 리턴값을 사용하지 않는다.

    function prompt(message?: string, _default?:string): string

    prompt() 함수는 매개변수로 message를 필요로 하고, 리턴값은 string 형식으로 나온다.

    function f(x) {
      return x * x;
    }
    
    console.log(f(3));        // 9

     

    함수 예제

    A부터 B까지 더하는 함수 만들기

    A부터 B까지라는 범위를 지정했을 때 범위 안에 있는 숫자를 모두 더하는 수. 매개변수로 A와 B를 받는다.

    function sumAll(a, b) {
      let sum = 0;
      for (let i = a; i <= b; i++) {
        sum += i;
      }
    	return `${a}부터 ${b}까지의 합: ${sum}`;
    }
    
    sumAll(1, 100);		// 1부터 100까지의 합: 5050
    sumAll(1, 500);		// 1부터 500까지의 합: 125250
    • 책을 안 보고 만들어봤는데 결과는 같다.

    일반적으로 숫자를 계산해서 출력할 때는 다음과 같은 형태를 띤다:

    function 함수(매개변수) {
      let output = 초깃값;
      처리
      return output;
    }

     

    최솟값 구하는 함수

    매개변수로 숫자들의 배열을 입력하면 배열 내부에 있는 숫자 중에서 가장 작은 값을 구하는 함수.

    function min(array) {
      let output = array[0];
      for (const item of array) {
        if (output > item) {
          output = item;
        }
      }
      return output;
    }
    
    const testArray = [27, 273, 32, 103, 275, 24, 57];
    console.log(`${testArray} 중에서 `);
    console.log(`최솟값 = ${min(testArray)}`)		// 최솟값 = 24

    변수 output을 배열의 첫번째 요소로 설정한 뒤, 배열 전체에 반복문을 돌려서 output보다 작은 값을 찾는 조건문을 사용했다.

     

    나머지 매개변수

    최솟값을 구하는 함수에서 변수를 배열로 받았는데, 숫자 자료형 여러개를 매개변수로 받으려면 나머지 매개변수를 사용하면 된다.

    function 함수 이름 (...나머지 매개변수) { }

    함수의 매개수 앞에 ... 를 입력하면 매개변수들이 배열로 들어온다. 매개변수가 고정적이지 않을 때에도 사용할 수 있다.

    function sample(...items) {
      console.log(items);
    }
    
    sample(1, 2);		// [1, 2]
    sample(1, 2, 3);		// [1, 2, 3]
    sample(1, 2, 3, 4);		// [1, 2, 3, 4]

    단순히 숫자 자료형으로 호출했던 매개변수가 배열 형태로 들어오는 것을 확인할 수 있다.

    function min(...items) {
      let output = items[0];
      for (const item of items) {
        if (output > item) {
          output = item;
        }
      } 
      return output;
    }
    
    console.log("min(52, 273, 32, 103, 275, 24, 57)");
    console.log(`= ${min(52, 273, 32, 103, 275, 24, 57)}`);		// = 24

    나머지 매개변수는 배열처럼 사용이 가능하다.

     

    나머지 매개변수와 일반 매개변수 조합하기

    나머지 매개변수는 말 그대로 나머지여서, 일반적인 매개변수와 조합해 사용할 수 있다.

    function 함수 이름(매개변수, 매개변수, ...나머지 매개변수) { }

    이 때 나머지 매개변수는 항상 마지막에 작성해야 한다.

    function sample(a, b, ...c) {
      console.log(a, b, c);
    }
    
    sample(1, 2);		// 1 2 []
    sample(1, 2, 3);		// 1 2 [3]
    sample(1, 2, 3, 4);		// 1 2 [3, 4]

    매개변수 a, b가 먼저 들어가고, 남은 것들은 모두 c에 배열 형태로 들어간다.

     

    매개변수 자료형에 따라 다르게 작동하는 함수

    function min(first, ...rests) {
      let output;
      let items;
      
      if(Array.isArray(first)) {
        output = first[0];
        items = first;
      } else if (typeof(first) === "number") {
        output = first;
        items = rests;    
      }
      
      for (const item of items) {
        if (output > item) {
          output = item;
        }
      }
      
      return output;
    }

    매개변수 자료형이 배열인지, 숫자인지에 따라 다르게 작동하는 함수이다.

    1. 매개변수 자료형 구분
      • Array.isArray() 메소드를 활용하면 자료형이 배열인지 아닌지 확실하게 확인할 수 있다.
      • typeof 배열 을 실행할 경우 object 라는 결과가 나온다. 간단한 예제에서는 확인할 수 있지만, 확실하게 알려면 Array.isArray() 메소드를 써야 한다.
    2. 매개변수 자료형이 배열일 경우
      • 매개변수 first 만 존재한다.
      • outputfirst 배열의 첫번째 요소로 지정하고, 반복문을 실행한다.
    3. 매개변수 자료형이 숫자일 경우
      • 매개변수로 숫자 자료형 first와, 배열 rests가 존재한다.
      • outputfirst로 지정하고, 배열 rests의 요소들과 비교하는 반복문을 실행한다.

     

    전개 연산자

    숫자로만 입력할 수 있는 함수에서, 배열의 요소들을 각각 입력하려면 전개 연산자를 활용하면 된다.

    함수이름 (...배열)
    function 함수 (a, b, c) {
      console.log(a);
      console.log(b);
      console.log(c);
    }
    
    const array = [1, 2, 3];
    
    함수(array[0], array[1], array[2]);		// 1 2 3
    함수(...array);		// 1 2 3

     

    기본 매개변수

    매개변수에 기본값을 지정할 수 있다.

    function sample (a, b=0, c=0)
    
    // 의미없는 기본값 지정:
    // function sample(a=0, b, c)

    매개변수는 왼쪽부터 입력하므로, 여러개의 매개변수가 있을 때 첫 번째 매개변수에만 기본값을 지정하는 것은 의미가 없다. b를 입력하려면 a를 반드시 입력해야 하기 때문이다.

     

    급여를 계산하는 함수

    매개변수로 시급과 시간을 입력받아 급여를 계산하는 함수

    function earnings(name, wage=8590, hours=40) {
      console.log(`${name}님의 급여 정보`);
      console.log(`- 시급: {wage}원`);
      console.log(`- 근무 시간: ${hours}시간`);
      console.log(`- 급여: ${wage * hours}원`);
    }
    
    earnings(Kim);
    // Kim님의 급여정보
    // - 시급: 8590원
    // - 근무 시간: 40시간
    // - 급여: 343600원
    
    earnings(Lee, 10000);
    // Lee님의 급여정보
    // - 시급: 10000원
    // - 근무 시간: 40시간
    // - 급여: 400000원
    
    earnings(Park, 10000, 52);
    // Park님의 급여정보
    // - 시급: 10000원
    // - 근무 시간: 52시간
    // - 급여: 520000원
    • Kim의 예시에선 매개변수 wagehours의 값이 입력되지 않아 기본값인 8590원과 40시간이 입력되었다.
    • Lee의 예시에선 매개변수 hours의 값이 입력되지 않아 입력 시급인 1000원과 기본값 40시간이 입력되었다.
    • Park의 예시에선 매개변수 모두가 들어왔다.

     

    함수 고급

    지정된 위치에 함수를 만들어야 하는 다른 프로그래밍 언어와는 달리, 자바스크립트는 함수를 중간에 만들 수 있다. 이는 비동기 프로그래밍을 이끌었다.

    콜백 함수

    함수 역시 하나의 자료형이므로, 매개변수로 함수를 전달하는 것이 가능하다. 매개변수로 전달하는 함수를 콜백함수라고 한다.

    function callThreeTimes (callback) {
      for (let i = 0; i = 3; i++) {
        callback(i);
      }
    }
    
    function print (i) {
      console.log(`${i}번 째 함수 호출`)
    }
    
    callThreeTimes(print);
    // 0번 째 함수 호출
    // 1번 째 함수 호출
    // 2번 째 함수 호출

    callThreeTimes()는 매개변수로 콜백함수를 받는다. callThreeTimes() 함수의 문장을 따라 callback(0), callback(1), callback(2) 가 실행이 되는데, 예제의 마지막 문장에서 콜백함수로 print()를 호출했으므로 실행되는 문장은 print(0), print(1), print(2)가 된다.

     

    콜백 함수를 활용하는 함수

    이하의 세 함수는 모두 배열에서 활용 가능하며, 완전한 형태는 다음과 같다.

    function (value, index, array) { }

    하지만 일반적으로 value나, value, index만 사용하는 경우가 많다. 매개변수는 모두 입력할 필요가 없고, 사용하고자 하는 매개변수의 순서만 맞춰서 사용하면 된다.

    forEach()

    배열 내부의 요소를 사용해 콜백 함수를 호출한다.

    map()

    콜백 함수에서 리턴한 값을 기반으로 새로운 배열을 만든다.

    filter()

    리턴하는 값이 true인 것만 모아서 새로운 배열을 만든다.

    화살표 함수

    단순한 형태의 콜백함수를 쉽게 입력하고자 할 때 화살표 함수를 사용한다.

    (매개변수) => {  } 불표현식 || 거짓일 때 실행할 문장.
    (매개변수) => 리턴값
    let numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
    
    numbers.filter((value) => value % 2 === 0)
    .map((value) => value * value)
    .forEach((value) => {
      console.log(value)
      // 0
      // 4
      // 16
      // 36
      // 64
    })

    number.filter((value) => value % 2 === 0)에서 짝수인 수로만 이루어진 배열이 만들어진다. 해당 배열이 map((value) => value * value)에서 각 수가 제곱된 배열이 만들어진다. 마지막으로 forEach((value) => { console.log(value) })에서 배열의 각 값이 콘솔에 출력된다.

    이런 식으로 메소드가 리턴하는 값을 기반으로 함수를 연달아 사용하는 것을 메소드 체이닝이라고 부른다.

     

    타이머 함수

    특정 시간마다 혹은 특정 시간 이후에 콜백 함수를 호출할 수 있는 함수로, 시간과 관련된 처리를 할 수 있다.

    setTimeout(함수, 시간)은 특정 시간 이후에 함수를 한 번 호출하며, setInterval(함수, 시간)은 특정 시간마다 함수를 호출한다.

    종료하고 싶을 땐 clearTimeout(타이머ID)setTimeout()을, clearInterval(타이머ID)setInterval()을 제거할 수 있다. 타이머 ID는 setTimeout()setInterval() 함수를 호출할 때 사용했던 식별자이다.

    const a = setInterval(() => {
      console.log(`야호!`);
    }, 1000)
    
    a;		// 1초마다 콘솔에 야호! 가 출력된다.
    
    const b = clearInterval(a);
    b;		// a의 실행을 멈춘다.

     

    함수 즉시 호출하기

    신형 브라우저의 경우, 블록 혹은 함수 블록을 통한 스코프를 생성해서 변수의 충돌을 막을 수 있다. 하지만 인터넷 익스플로러같은 구형 브라우저에서 쓰는 var 키워드는 블록을 통해서는 불가능하고, 함수 블록을 통해서만 변수의 충돌을 막을 수 있다.(심지어 Babel도 제대로 변환해주지 못한다!)

    변수의 충돌을 막기 위해 함수를 만들자마자 즉시 호출할 수 있도록 다음과 같은 형태로 작성한다.

    (function () { }) ()
    let pi = 3.14
    console.log(`파이는 ${pi}`)		// 파이는 3.14 
    
    let pi = 3.141592
    	// Uncaught SyntaxError: Identifier 'pi' has already been declared
    
    (function() {
        let pi = 3.141592;
        console.log(`파이는 ${pi}`)		// 파이는 3.141592
    }) ()

    pi 식별자가 두 번 사용되었는데, 함수 블록 밖에선 에러가 나지만, 즉시 호출 함수에선 의도한 대로 다르게 작동한다.

    • 크롬 콘솔에선 let pi = 3.14; let pi = 3.141592; 문장이 에러가 안 떠서 const인데 오탈자가 난 건가 생각했는데, nodeJS 환경이나 html에 script로 import했을 때는 에러가 났다. let pi = 3.14; pi = 3.141592;가 맞는 구문.

     

    엄격 모드

    코드를 엄격하게 검사하는 모드

    'use strict'
    문장;

    자바스크립트는 오류를 어느 정도 무시하고 넘어가기 때문에, 기본값을 느슨한 모드(sloppy mode)라고 부르기도 한다. 이는 코딩하기엔 쉽지만 실수를 하기도 쉽다.

    let mistypedVariable = 10;
    
    function example() = {
      mistypedVaraible = 17;
    }
    
    example();
    console.log(mistypedVariable)		// 10 (오타 때문에 17로 값이 변경되지 않앗다.)

    느슨한 모드에선 let 키워드를 사용하지 않고 변수를 선언해도 에러가 발생하지 않는다. 이러면 오타 하나 때문에 의도에서 엇나가게 된다. 엄격 모드에선 이렇게 선언 없이 변수 사용하는 등의 실수에 에러를 띄운다.

    일반적으로는 엄격 모드를 사용하는 것이 좋다. 엄격 모드는 문서 전체에 적용할 수도 있지만, 코드 블록에만 적용할 수도 있다. 이를 이용해 즉시 호출 함수를 만들고, 해당 함수 블록의 가장 위쪽에서 엄격 모드를 적용하는 경우가 많다. 이러면 해당 함수 내부에만 엄격 모드가 적용된다.

    엄격함의 기준은 시간에 따라 달라지므로, MDN의 Strict mode를 참고한다.

     

    익명 함수 vs. 선언적 함수

    익명 함수와 선언적 함수는 사용하는 상황이 비슷해서, 개인 혹은 팀의 선호에 맞게 사용하면 된다. 하지만 최근에는 안전 등의 이유로 익명 함수 사용을 선호하는 추세이다.

    익명 함수는 우리가 읽는 것처럼 위에서 아래의 방향으로 차례대로 코드가 실행된다. 반면 선언적 함수는 코드 블록을 읽어들일 때, 함수를 먼저 생성하고 이후에 코드가 실행된다. 즉, 코드 내에서 함수 호출을 먼저하고 그 뒤에 생성을 하더라도 프로그램은 문제 없이 실행된다.

    선언적함수();		// 생성보다 호출을 먼저 해도 작동한다.
    
    function 선언적함수() {
      console.log("1번째");
    }
    function 선언적함수() {
      console.log("2번째");
    }
    
    // 2번째

    우리가 읽는 순서와 다르게 작동하고, 같은 이름의 함수가 있으면 덮어씌운다.

    함수 = function () {
      console.log("익명 함수");
    };
    
    function 함수() {
      console.log("선언적 함수");
    }
    
    함수();		// 익명 함수

    코드가 실행되기 전에 선언적 함수가 먼저 생성되고, 그 다음에 익명 함수 생성이 실행되며 덮어 쓴다. 코드를 작성한 순서와 무관하게 익명 함수가 출력된다. 의도치 않게 덮어 쓰는 것은 매우 위험하므로, 코드의 순서대로 함수를 생성하고 실행할 수 있는 익명 함수가 선호된다.

    같은 이름의 선언적 함수를 여러 블록에서 사용할 경우에도 문제가 된다. 블록이 나뉘어진 경우 선언적 함수의 실행 흐름을 예측하는 것이 힘들어진다.

    선언적 함수와 마찬가지로, 과거 자바스크립트의 키워드인 var는 덮어 쓰는 문제가 발생한다. 이를 방지하기 위해서는 letconst 키워드를 사용하면 된다.

    댓글

Designed by Tistory.