TIL 13 | 함수 (Function)

ElenaPark·2021년 2월 24일
0

JavaScript

목록 보기
10/14
post-thumbnail

함수란?

  • 프로그래밍에서 매우 중요한 역할 수행 (fundamental building block)
  • 여러번 재사용이 가능한 하위 프로그램(subprogram)
  • 하나의 일(task) 또는 값의 계산을 수행하는 역할
  • JavaScript에서의 함수는 object(객체)이다
  • JavaScript는 절차적 언어로서 함수가 매우 중요한 역할을 담당 (class 문법이 추가되어 있기는 하지만, 순수한 객체지향 언어가 아닌 prototype 베이스의 가짜의 객체지향 언어라고 볼 수 있다)

함수 선언문 (Function declaration)

작성법

function name(parameter1, parameter2) {
   body 작성
   return;
}
  • 하나의 함수는 하나의 task만 수행한다.
  • 함수명을 만들 때는 command,verb 형태로 작성한다.
  • 하나의 함수에서 여러가지 일을 하고 있지는 않은지 계속 확인해야 한다. (예를들어 함수명 createCardAndPoint라는 함수는 createCard, createPoint 두 개의 함수로 쪼개어 만드는게 좋다.)

함수에 전달하는 parameter가 없는 경우

function printHello() {
  console.log("Hello");
}

printHello();

함수에 parameter를 전달하는 경우

function log(message) {
  console.log(message);
}

log("Hi!");

자바스크립트는 type이 없으므로, 아쉽게도 함수 자체에 parameter로 어떤 type의 값(string인지, number인지..)을 전달해야하는지 명확하지 않다. 따라서 사용자가 적합하지 않은 type의 값을 전달할 수도 있음을 유의해야한다.

TypeScript의 등장
JavaScript를 빠르게 제작하는 과정에서 발생하는 단점들을 보완하기 위해 만들어진 언어.

function log(message: string) {
  console.log(message);
}

TypeScript는 JavaScript에서 type을 얹어 사용할 수 있는 언어인데, 위의 간단한 예로 확인할 수 있듯이, parameter : type 형태로 지정해줄 수 있다.

여기서 만약 return값을 number로 받고 싶다면 아래와 같이 작성할 수 있다.

function log(message: string): number {
  console.log(message);
  return 0;
}

TypeScript는 언제 쓸까?

  • 규모있는 프로젝트 진행할 경우
  • 현업에서 다양한 개발자들과 일을 할 경우
  • 사용자가 작성한 것을 추후 라이브러리 형태로 API를 제공해야할 경우

Parameters

원시타입 파라미터 (primitive parameters)

메모리에 값이 저장되어있으므로 값 자체를 전달

객체 파라미터 (object parameters)

메모리에 참조 값(reference)이 저장되어있으므로 참조 값 전달

function changeName(object) {
  object.name = "coder";
}

const elena = { name: "elena" };
changeName(elena);
console.log(elena); // { name: "coder" } 

default parameters (ES6에 추가된 기능)

함수 호출 시 필요한 인자를 넣지 않았을 경우, 기본적으로 출력되는 defalut parameters를 함수 선언 시 설정해놓을 수 있음.

function showMessage(message, from = 'unknown') {
  console.log(`${message} by ${from}`);
}

showMessage("Hi"); // Hi from unknown

위와 같이 parameter = 'defalut value'를 설정하는 방식으로 사용자가 값을 전달하지 않았을 때 자동으로 defalut로 설정한 값이 출력되도록 설정할 수 있다.

rest parameters (ES6에 추가된 기능)

parameter로 들어가는 값에 ...라는 점이 세개가 붙게 되면 rest parameter라고 불린다. 이는 배열 형태로 전달되는 값이다.

function printAll(...args) {
  for (let i = 0; i < args.length; i++) {
    console.log(args[i]); 
  }
}

printAll("elena", "paulina", "jorge");

위와 같이 함수 호출 시 총 세개의 인자를 전달했으므로 ...args에는 ['elena','paulina','jorge']의 배열 형태로 파라미터가 전달이 된다. 이에 반복문 for문을 이용하여 loop를 돌게 되어 콘솔창에는 각 값이 차례대로 출력된다.
// elena
// paulina
// jorge

배열에 반복문을 돌 때 for문만 사용할 수 있는 것이 아니라, 간단하게 for of문 또는 배열의 forEach 함수를 이용하여 반복문 작성이 가능하다.
1) for of

function printAll(...args) {
  for (const arg of args) {
    console.log(arg);
  }
}
printAll("elena", "paulina", "jorge");

2) forEach

function printAll(...args) {
  args.forEach((arg) => console.log(arg));
}
printAll("elena", "paulina", "jorge");

로컬 스코프 (local scope)

밖에서는 안이 보이지 않고, 안에서만 밖을 볼 수 있다.

즉, block 스코프에서는 global 스코프의 변수를 참조할 수 있지만 그 반대는 불가능하다.

let globalMessage = "global"; // global variable

function printMessage() {
  let message = "hello"; // local variable
  console.log(message);
  console.log(globalMessage);
  function printAnother() {
    console.log(message);
    let childMessage = "helloChild";
  }
  printAnother(); // hello
  console.log(childMessage); // Uncaught ReferenceError: childMessage is not defined
}

printMessage();

Closure란?
위와 같이 중첩된 함수에서 자식의 함수가 부모 함수에 정의된 변수에 접근이 가능한 것을 closure라고 함.

값의 리턴 (Return a value)

함수에서는 parameters로 특정 값을 전달받아 body에서 해당 값을 계산한 값을 return할 수 있음.

function sum(a, b) {
  return a + b;
}

const result = sum(1, 2);
console.log(`sum: ${sum(1, 2)}`); // sum: 3

그러나 위와 같이 return 이 없는 함수의 경우 자동으로 return값은 undefined를 가짐.(생략되어있음)

Early return, early exit!
block scope 안에 조건이 맞는 경우의 긴 로직을 넣는 것보다, 조건이 맞지 않는 경우에 대한 로직을 넣어 맞지 않으면 빠른 리턴을 하여 함수를 종료하는 식으로 코드를 작성하는 것이 가독성이 더 좋음
bad example

function upgradeUser(user) {
  if (user.point > 10) {
    // 긴 업그레이드 로직이 들어감
    // block 안에서 로직을 많이 작성하면 가독성이 떨어짐
  }
}

good example

function upgradeUser(user) {
  if (user.point <= 10) {
    // 조건이 맞지 않을 때는 빠른 return해서 함수를 종료
    return;
  }
   // block 밖에서 조건이 맞을 때만 긴 업그레이드 로직을 넣음
}

함수 표현식 (Function expression)

함수는 일급객체(First-class function)이다.

즉, 함수는 다른 변수와 마찬가지로 변수에 할당이 되고
다른 함수의 parameter로 전달이 되며,
리턴값으로도 리턴이 된다.
=> 이것을 가능하게 하는 것이 함수 표현식임.

작성법

const print = function () {
  console.log("print"); // print
};

print();

const printAgain = print;
printAgain();

함수를 선언함과 동시에 바로 print라는 변수에 할당한다.
표현식의 경우 함수에 아무런 이름이 없으며 이렇게 함수에 이름이 없는 경우 익명함수(anonymous function)라고 한다.(사용자가 원한다면 function 옆에 함수명을 작성할 수도 있다. 이 경우 기명함수(named function)라고 한다.)

할당한 변수를 기존에 함수 선언식에서 호출했던 것처럼 호출하면 함수가 실행된다.

마찬가지로 printAgain이라는 변수에 print변수를 다시 할당하니 printAgain도 print를 호출했을 때와 동일한 결과값을 가진다.

함수 선언식(function declaration)과 함수 표현식(function expression)의 가장 큰 차이?
함수 선언식은 호이스팅(hoisting: 선언이 제일 위로 끌어올려지는 것)으로 인해 함수가 선언되기도 전에 호출이 가능하나, 함수 표현식은 변수에 함수가 할당된 다음에야 호출이 가능하다.
즉, 함수 선언식은 함수가 선언되기도 전에 호출이 가능하며, 함수 표현식은 선언되기 전에 호출이 불가능하다.

함수 표현식을 사용한 콜백 함수

function randomQuiz(answer, printYes, printNo) {
  if (answer === "elena") {
    printYes();
  } else {
    printNo();
  }
}

const printYes = function () { // 익명함수
  console.log("yes!"); 
};

const printNo = function print() { // 기명함수
  console.log("no!");
};

randomQuiz("elena", printYes, printNo);
randomQuiz("paulina", printYes, printNo);

위의 예시에서 printNo변수에는 print라는 기명함수를 할당했는데,
보통 기명함수를 쓰는 것은
1) 디버깅할때 함수명이 노출되도록 하기 위함
2) 함수 안에서 스스로 함수 자신을 호출할 때(recursion) 사용함

화살표 함수(Arrow Function)(ES6 기능)

작성법

// 함수 표현식 예시
const simplePrint = function () {
  console.log('simplePrint');
}

// 화살표 함수 예시 
const simplePrint = () => console.log('simplePrint');

위의 비교 예시를 보면, 화살표 함수에서는 함수 표현식 예시와는 달리 function 키워드가 없고 block {} 대신 => 화살표가 사용된다. 만약 조금 더 복잡한 코드를 작성한다면 화살표 다음에 block {}을 사용한다.

// 함수 표현식 예시
const add = function (a, b) {
  return a + b;
};

// 화살표 함수 예시
const add = (a, b) => a + b;

위의 예시는 return값을 가지는 경우이다.
함수 표현식에서는 return 키워드가 항상 필요한 반면, 화살표 함수의 경우 block {}을 사용하지 않을 경우 return 키워드는 작성하지 않는다. 마찬가지로 block {}을 사용하게 되면 return 키워드를 함께 작성해야한다.

즉시 실행 함수 (IIFE: Immediately Invoked Function Expression)

함수를 선언하고 따로 호출해주는 것이 아니라,
선언함과 동시에 바로 호출을 해주는 방법.

작성법

(function hello() {
  console.log("IIFE");
})(); // IIFE

함수를 바로 호출하고 싶을 때 유용하게 사용될 수 있음.

참고자료

드림코딩엘리 함수편

profile
Front-end 개발자입니다.

0개의 댓글