Function, 함수의 선언과 표현

WorldWannyWeb.·2020년 9월 20일
0
post-thumbnail

매일매일 사용하는 다양한 프로그램들은 각각 저마다 제공하는 고유한 기능이 있다. 프로그램안에서도 각각 저마다의 기능을 수행하는 함수들이 존재한다. Procedure language같은 경우, 즉 절차적 언어 같은 경우는 함수가 프로그램에서 굉장히 중요한 기능을 담당한다. 자바스크립트에 추가된 class는 prototype을 기반으로 한 가짜의 object orient이다. 그래서 자바스크립트도 절차적언어 중 하나라고 생각하면 된다. 그래서 functiond이 굉장히 중요한 기능을 담당하고 있기 때문에 때로는 sub-program이라고도 부른다. 프로그램안에서 각각의 작은 단위의 기능들을 수행하는 것이 function이다. function은 대체적으로 input 즉, parameter들을 받아서 이것들을 잘 처리한 후에 output 즉, return 한다. 언어자체에 존재하는 function을 쓸 때, 또는 API(Application Programming Interface)를 쓸때, 함수명을 보고 어떤 일을 하는지 알 수 있고, 전달해야하는 parameters들이 무엇인지 어떤 값이 리턴되기를 바라는지를 이런 interface들을 보고 알 수 있다. 그래서 함수는 input, output이 중요하고, 함수명을 잘 정하는 것이 중요햐다.

1. Function

  • fundamental building block in the program
    매우 기본적인 building 프로그램
  • subprogram can be used multiple times
    서브프로그램이라고도 불리고, 여러번 사용이 가능
  • performs a task or calcultates a value
    한가지의 테스크나 어떤값을 계산하기 위해 사용

1. Function delaration / 자바스크립트에서 함수를 정의하는 방법

function name(param1, param2){
  body...return;
}
// function이라는 키워드를 적고 함수의 이름을 정한 후
// ()안에 parameters를 쭉 나열한 다음에 
// 함수안에 기본적인 business logic을 작성한 다음 
// return; 해주면 된다. 

중요 포인트! one function === one thing
하나의 함수에는 한가지 일만 할 수 있도록 해야한다. 함수이름을 정할때는 무언가를 동작하는 것이기 때문에 command ex)doSomething 나 verb형식으로 적어야한다.

자바스크립트에서 function은 object이다. object로 간주되어지기 때문에 변수에 할당할 수 도 있고, parameter로 전달이 되고 함수를 return 도 가능한것!

function printHello(){
// function이라는 키워드를 적고 함수의 이름은
// printHello, parameter는 하나도 받지 않음
  console.log('Hello');
  // console에 Hello 를 출력 
}
printHello(); // 함수호출 // Hello가 출력됨

사실 위의 함수는 쓸모가 없다. 계속 Hello만 출력하기 때문이다. 그럼 조금 더 유용한 함수를 만들어보자.

function printHello(){
  console.log('Hello');
}
printHello(); // Hello가 출력됨
function log(message){ 
  // parameter로 message를 전달하면 
  console.log(message);
  // 전달된 message를 console로 
  //화면에 출력하게 만든다.
}
log(Hello@);
// 이렇게 log함수를 호출하면서 우리가 원하는 
// message를 출력할 수 있다. 

^ 위의 function에서 함수자체의 interface를 보면 message가 string을 전달하는지 숫자로 전달하는지 명확하지 않아서 숫자로도 저장이 가능하다. 자바스크립트가 type이 없기 때문이다.


1. Parameters

primitive parameters: passed by value

  • primitive type같은 경우 메모리에 value가 그대로 저장되어있기 때문에 value가 전달이 된다.
    object parameters: passed by reference
  • object같은 경우에는 메모리에 reference가 저장이 된다. 그래서 reference가 전달이 된다.
function changeName(obj){
//changeName이라는 함수는
  obj.name = 'coder'; 
  // 전달된 obj의 이름을 'coder'로 
  // 무조건 변경하는 함수이다.
}
const ellie = { name: 'ellie' }; 
// ellie라는 const를 정의한다음에 ellie라는 object를
// 만들어서 할당해주면 memory에는 object가 만들어진
// reference가 메모리에 들어가게 되고, referece는 
// 바로 이 object 'ellie'를 메모리 어딘가에 
// 가리키고 있다. 
changeName(ellie); 
// changeName(ellie)를 전달하게 되면 전달된 ellie
// 즉, ellie가 가리키고 있는 곳의 이름을 coder로
// 변경하게 되는 것.
console.log(ellie); 
// 그것을 출력하게 되면,
// coder로 변경되어있는 것을 발견할 수 있다. 
// {name: 'coder'}

Object는 reference로 전달되기 때문에, 함수안에서 object의 값을 변경하게 되면, 그 변경된 사항에 그대로 메모리에 적용이 되기 때문에, 나중에 변경된 사항들이 확인이 가능하다.


2. Default parameters (added in ES6)

ES6에 새롭게 추가된 것

function showMessage(message, from){ 
// showMessage function을 봐보자.
// (1) message와 from,두개의 parameters를 받아옴 
// message가 누구로부터 왔는지 출력가능 
  console.log(`${messege} by ${from}`); 
  //(3) Hi! by undefined  
  //(4) message는 잘 전달 되었지만,
  //   from이 정의되어 있지 않아서 undefiend로
  //   나오는 것을 알 수 있다. 
}
showMessage('Hi!'); 
// (2)보면 showMessage를 호출할때 
// 'Hi!' 한개의 메세지만 전달된것을 확인할 수 있다. 
///////////////////////////
// <과거> 
function showMessage(message, from){ 
  if(from === undefined){ 
    from = 'unknown'; 
    // 과거에는 parameter가 잘 전달되지 않을때를
    // 대비해서  from이 undefiend이면 
    // 우리가 원하는 가장 기본적인 unknown이라는
    // string을 추가하자 해서 적었지만 
  }
  console.log(`${messege} by ${from}`); 
}
showMessage('Hi!'); 
///////////////////////////
//< 현재 > 
function showMessage(message, from = 'unknown'){
// (7)지금은 parameter 옆에 원하는 default값을 지정해
// 놓으면 사용자가 parameter를 전달하지 않을때 
// 그 값이 대체되어서 사용된다. 
  console.log(`${messege} by ${from}`); 
}
showMessage('Hi!');

3. Rest parameters (added in ES6)

function printAll(..args){ 
// (1) ...세개를 작성하게 되면 rest parameters라고
// 불리는데 바로 배열형태로 전달되게 된다.
// (3) args는 세개의 값을 담고 있는 배열인것 
  for(let i = 0; i < args.length; i++){ 
  // (4) for loop을 이용해서 처음부터 args의 개수만큼
  //돌리면서 출력하는 것. 
    console.log(args[i]);
  }
  // 배열같은것 출력할때 for loop를 이용할 수 있지만,
  // 간단하게 for .. of문으로 간단하게 출력할 수도 있다. 
  for(const arg of args){ 
  // args에 있는 모든 것들이 차례대로 하나하나씩
  // arg부분에 지정이 되면서 
    console.log(arg); 
    // console에 출력되는것. 
  }
  // 더 간단하게 하고 싶다면, 아래처럼
  // 배열에서 .forEach라는 함수형 언어를 
  // 이용해서 출력해도 괜찮다. 
  args.forEach((arg) => console.log(arg));
}
printAll('dream', 'coding', 'ellie'); 
// (2) printAll이라는 함수를 호출할때 
// parameters를 세개를 전달했는데

아까 function은 object의 일종이다 라고 했는데 그 뜻은, printAll함수를 선언했을때, console 창에서 printAll옆에 . 을 누르면 함수가 object로 전환되기 때문에 printAll의 속성값들을 확인해 볼 수 있다.


4. Local Scope

block level scope과 global level scope에 대해 설명했는데, closure 나 lexical environment는 scope을 좀 더 상세하게 들어간 것이다.
딱하나의 원칙만 기억하자. scope은 '밖에서는 안이 보이지 않고, 안에서만 밖을 볼 수 있다' 라고 기억하면 된다.
블럭{} 안에서 변수를 선언하게 되면 local variable (지역변수)이다. 메세지를 밖에서 출력하게 되면 에러가 발생한다. 안에서는 밖에 있는 globalmessage를 볼수있고, 출력도 가능하다. 어느곳에서나 가능하다.

let globalMessage = 'global'; // global variable
function printMessage(){
  let message = 'hello';
  // 함수나 if 안에서 이렇게 {}안에서 
  // 변수를 선언하게 되면, 이것은 지역변수이다.
  // 지역변수는 지역적인것이라서 
  // `안에서만 접근이 가능`하고,
  console.log(message); // local variable
  console.log(globalMessage);
  // 하지만 {} 안에서는 globalMessage를 볼 수 있고,
  // 출력이 가능한것도 확인할 수 있다. 
  function printAnother(){
  // 지금처러 함수안에서 또다른 함수를 선언할 수 도 있다.
  // printMessage함수 안에 printAnother이라는 함수가
  // 들어있다. 
  // 자식 즉, printAnother 함수는 
  // 부모,즉, printMessage 함수에서 정의된 메세지들을
  // 확인할 수 있다. 하지만 자식안에 정의된 childeMessage를 보려한다면,
  // error가 발생할 것이다. 
    console.log(message);
    let childeMessage = 'hello';
  }
}
printMessage();
  console.log(message);
// 메세지를 이렇게 밖으로 출력하면 error가 발생

자바스크립트에서 scope란 안에서는 밖이 보이지만 밖에서는 안이 보이지 않고 접근이 안된다. 그리고 중첩된 함수에서 자식의 함수가 부모함수에 정의된 변수들을 접근이 가능한것들이 closure이다.


5. Return a value

function sum(a, b){  
// 함수에서는 parameters들로 값을 전달받아서 계산된 값을
  return a + b; // return 할 수 있다. 
}
const result = sum(1, 2);  // 3
// sum이라는 함수를 호출하게 되면,
// 1과 2를 더해서 3이 return되는 것을
// 확인 할 수 있다. 
console.log(`sum: ${sum(1, 2)}`);

함수 안에 return 타입이 없는 함수들은 return undefined; 가 들어가 있는 것과 같다. 그래서 생략이 가능하다. 그래서 모든 함수는 return undefined가 들어가 있거나 값을 return할 수 있다.


6. Early return, early exit

Bad Example

function upgradeUser(user){
//upgradeuser라는 함수 안에서 
  if(user.point > 10){ 
  // user.point가 10이상일때만
  // 업그레이드하는 logic이 있다면 
   //long upgrade logic... 
  // {}블록안에서 logic를 많이 작성하면
  // 가독성이 떨어진다.
  }
}

Good Example

function upgradeUser(user){ /
  if(user.point > 10){ 
  // 조건이 맞지 않을때는 빨리 return 해서
  // 함수를 종료하고, 조건이 맞을때만 그 다음에 와서
  // 필요한 로직들이 와서 실행되는 것이 좋다.
  // 조건이 맞지 않는 경우, 값이 undefined 인 경우,
  // 값이 -1인경우 빨리 return하고 
  // 필요한 logic은 그 뒤에 만드는 것이 좋다.  
    return; 
  } 
    //long upgrade logic... 
}

2. Function Expression

Function을 어떻게 선언할 수 있는지 알아보았다.
이제는 Function Expression에 대해서 알아보자.

First-class function

functions are treated like any other vairable
can be assigned as a value to variable
can be passed as an argument to other functions
can be returned by another function

function은 다른 변수와 마찬가지로, 변수에 할당이 되고, function에 parameter로 변환이 되고, return 값으로도 return이 된다. 그것들을 가능하게 하는 것이 function expression 이다.

  • Function Expression
  1. a function declaration can be called earlier than it is defined.(hoisted)
  2. a function expression is created when the execution reaches it.
const print = fuction (){  
// 함수를 선언함과 동시에 바로 print라는 변수에 할당한다. 
// 이렇게 function에 아무 이름이 없고, function이라는
// 키워드를 이용해서 parameter와 {}을 이용한것을
// 볼 수 있는데, 함수에 이름이 없는 것을 
// `anonymous function` 이라고 부른다.
// 이름 없이, 필요한 부분만 적어서 할당할 수도 있고, 
// 원하면 함수의 이름도 작성을 할 수있다. 
// function에 이름이 있는 것을 
// named function이라고 한다. 
  console.log('print');
};
print(); 
// 함수를 print에 할당하게 되면 
// print라는 변수에 함수를 호출하듯이 
// 호출하게 되면 바로 print가 출력이 된다. 
const printAgain = print; 
// 다시 다른 변수에 또 할당을 하게 되면
// 결국 printAgain은 함수를 가리키고 있어서 
printAgain(); 
// 다시 함수를 호출하는 것처럼 부르면,
// print가 출력이 된다.  
const sumAgain = sum;
console.log(sumAgain(1, 3));

function declaration과 function expression의 차이를 보면, function expression은 할당이 된 다음부터 호출이 가능한 반면에 즉, print를 선언하기 전에 호출하게 되면, 당연히 에러가 나올것이다. 하지만 function declaration은 hoisted가 된다. 즉, 함수가 선언되기 이전에도 위에서 sum 이라는 함수를 정의하기도 전에 호출이 가능하다는 뜻이다. 자바스크립트의 엔진이 선언된 것을 제일 위로 올려주기 때문이다.


2. Callback function using function expression

function randomQuiz(answer, printYes, printNo){
// randomQuiz라는 function을 보면
// answer와 정답이 맞을때 호출하게되는 함수printYes와
// 그리고 정답이 틀리면 호출하게되는 함수printNo,
// 함수 두개를 전달해주는데, 함수를 전달해서 
// '야, 너가 상황이 맞으면 너가 원하면 이 전달된 함수를
// 불러' 라고 전달하는 것을 callback function
// 이라고 한다.
// 즉, pYes, pNo 이 두 callback 함수가 parameter로
// 전달되어서, if문의 조건문 answer가 luv you 일때만,
// printYes라는 callback함수를 호출하게 되고,
// 정답이 아니면, printNo라는 callback함수를 호출하게된다.
// 이 함수를 호출하려면, answer와 printYes,printNo
// 이 두가지의 expression을 전달해야한다. 
  if(answer === 'luv you'){
    printYes();
  } else {
    printNo();
  }
}
const printYes = function(){
// printYes라는 변수에 'yes!'를 출력하는
// 함수를 할당해놓는다.
// printYes를 보면 anonymous function를 할당한것을
// 볼 수 있다. 
  console.log('yes!');
};
const printNo = function print(){
// printNo라는 변수에 'no!'를 출력하는
// 함수를 할당해놓는다. 
// printNo는 print라는 이름이 있는 함수 즉,
// named function을 할당한것을 볼 수 있다.
// 이처럼 expression에서 이름을 쓰는 경우는 
// debugging할 때, 디버깅의 stack traces에 
// 함수 이름이 나오도록 한 것이다.
  console.log('no!');
  print();
  // 또는 함수 안에서 자신 스스로 또 다른 함수를 호출할때
  // 계속계속 돌아가는 것을 볼 수 있는데,
  // 이것을 recursion이라고 한다. 이러면 프로그램이 죽으므로
  // 정말필요할때, 예를들어,피보나치 수열이나, 반복되는
  // 평균값을 계산할 때 등에 사용할 수 있다.
  // 함수를 무한대로 호출하게 되면
  // callstack size exceeded 즉, callstack이
  // 꽉 찼어!!라고 error가 발생한다.
};
randomQuiz('wrong','printYes','printNo'); // no!
randomQuiz('love you','printYes','printNo'); // yes!
// 그래서 randomQuiz를 호출할때, answer과 printYes, 
// printNo의 callback함수들을 각각 전달하게 된다.
// 정답이 맞으면 yes를 호출하고 아니면 no를 호출해서
// yes, no가 출력된것을 볼 수 있다. 

4.Arrow function

always anonymous
Arrow function은 함수를 매우 간결하게 만들어준다. 항상 이름이 없는 함수이다.

const simplePrint = function (){
  console.log('simplePrint!');
}

Arrow function은 function부분을 지우고, {}도 지운 후에, 한줄에 묶어서 => arrow만 그려주면 끝!

const simplePrint = () => console.log('simplePrint!');

자, 여기 아래에 Arrow function이 있다.

const add = (a , b) => a + b;

위의 함수를 function expression으로 바꾸게 되면, 아래처럼 쓸 수 있다.

const add = function (a , b) {
  return a + b;
}

한줄인 경우에는 {}없이 가능하지만, 함수안에서 조금 더 다양한 일들을 해야해서 block이 필요하다면, 아래처럼 {}을 넣어서 처리할 수 있다. 대신에 {}을 쓰게 되면, return이라는 키워드를 이용해서 값을 return해 주어야한다.

const simpleMultiply = (a, b) => {
  // do sth more
  return a * b;
};

IIFE: Immediately Invoked Function Expression

function hello(){
  console.log('IIFE');
}
hello();
// 함수를 선언하게 되면 나중에 
// 따로 hello(); 라고 함수를 호출해주어야한다. 
/////////////////////////////////////
// 그렇지만, 선언함과 동시에 바로 호출하려면
(function hello(){
  console.log('IIFE');
})();
// 함수의 선언을 ()로 묶은후 함수를 호출하듯이
// ();를 붙이면 함수를 바로 호출할때 사용할 수 있다.
profile
와니완의 월드와이드와니웹🐥

0개의 댓글