본문 바로가기

JavaScript

[JS] Arrow Function은 무엇인가? 함수의 선언과 표현

*  Function
-  기본적인 빌딩블럭
-  subprogram이라고도 불리며 여러번 재 사용이 가능하다.
 한가지의 task(작업)나 값을 계산하기 위해 쓰인다.

 

 


1. Function declaration (함수 선언)

-  function 키워드 이용, 이름(name)지정, (  )에 parameter(인자) 나열, {  } body 로직 작성, return해주면 된다.
-  하나의 함수는 한 가지의 일만 한다.
-  함수이름 작성시, 동사형태로 이름을 지정해야한다.
ex)
createCardAndPoint 라는 함수 이름이 있다고 보자.
우린 이것을 creteCard 혹은 createPoint로 나눠야 할지 말지에 대해 고민해야한다.
-  함수는 object이다.


ex)
function printHello() {
console.log('Hello');
}    --->함수 지정
printHello();  --->함수 호출

 

 

-  log함수 호출하면 원하는 메세지 출력 가능.

ex)

log('Hello@');


-  JS에서는 type이 없어서 숫자인지 string을 전달해야하는지 명확X. (만든사람이 대충만들어서)
그래서 사용하는 사람이 숫자로 전달할 수 있음.
ex)
log(1234);


-  숫자가 문자열로 변환돼서 로그가 출력되기때문에 상관없지만 다른 함수에서 타입이 중요한 경우 JS는 난해할 수 있다.

 



*  TS(TypeScript)




: 규모 있는 프로젝트, 작성한 것을 library 형태로 api로 제공할땐 TS를 사용하는 게 좋다. (함수의 interface만 봐도 무엇을 의미하는지 확실하게 확인하고 쓸 수 있기 때문이다. JavaScript에서는 명시X)
parameter이나 type을 명시하도록 돼있다.



2. Parameter (매개변수)

// 2. Parameters
// premitive parameters: passed by value
// object parameters: passed by reference
function changeName(obj) {
    obj.name = 'coder';
}
const ellie = { name: 'ellie'};
changeName(ellie);
console.log(ellie);

-  premitive:  메모리에 value가 저장
-  object:  메모리에  referece가 저장

 

-  changeName()은 전달된 obj의 이름을 coder로 변경하는 함수.
ellie라는 const 정의 후 ellie라는 obj를 만들어 할당하면
메모리에는 obj가 만들어지 ref가 메모리에 들어감. 이 ref는는 이 obj를 메모리 어딘가에 가리키고 있다
obj는 ref로 전달되기 때문에 함수 안에서 obj의 값을 변경하게되면
그 변경된 사항이 그대로 메모리에 적용 되기에 추후에 변경된 사항들이 확인 가능하다. 

 

 


3.Default parameters

-  function을 실행했을 때 parameter값을 실수로 안넣거나 했을 경우를 대비해 parameter의 기본값(default값)을 설정할 수 있다.
-  default parameter 선언은 등호(=)를 사용하면 된다.
-  그러면 function 실행시 parameter가 정의되지 않았을 때 default parameter가 발동된다.
-  default parameter는 단순한 값 뿐만 아니라 연산자, 함수 등을 이용해 다양하게 설정할 수 있다.

:  showMessage에 두 가지 parameters (message, from)이 있다. 그래서 이 message가 누구로부터 왔는지(from) 출력.
:  (맨 아래)  showMessage 호출할 때 ('Hi!')라는 메세지만 전달되는 것 확인할 수 있다. 메시지는 출력되지만 from이 정의되지 않아undefined로 나온다.

===> Hi 라는 하나의 인자만 전달되었고, 2번재 인자는 from에 대해서 그 어떤 것도 전달되지 않았기 때문에

 


-  parameter값이 정의되지 않을 경우, 

if (from === undefined) {
from = 'unknown';
}

(그 중 이부분)
-  예전엔 from이 undefined이면 unknown이라는 기본적인 string을 추가하자 라고 해서 unknown이라고 출력되는 것을 확인할 수 있었다.

- 하지만 지금은 이렇게 적지 않아도 

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

이라고 지정하면 사용자가 parameter를 전달하지 않을때 이 값(unknown)이 대체되어 사용된다.

 

 


4. Rest parameters (added in ES6)

: 말 그대로 rest(여분, 나머지) parameter라는 의미.

: . . .을 사용한다.

:배열 형태로 전달

// 4. rest parameters (added in ES6)
function printAll(...args) {
    for (let i = 0; i < args.length;) i++) {
        console.log(args [i]);
    }
}
printAll('dream', 'coding', 'ellie');

-  printAll이라는 함수를 호출할 때 3개의 인자(dream, coding, ellie)를 전달한다.

-  . . . args = 3개의 값이 담겨져있는 배열.

-  for문 이용해서 "처음부터 arg의 갯수만큼 돌면서

1번째 인자(dream), 그 다음에는 

2번째 인자(coding), 그 다음엔

3번째 인자(ellie) 를 출력한다.

===> for문을 통해 모든 요소를 출력해도 되고 그 외 다른 형태로도 출력 가능하다.

 

 

-<배열 출력하는 법>

:  for이용해도 되지만  for .. of라는 문법으로 더 간단하게 할 수 있다.

for (const arg of args) {
	console.log(arg);
    }

-  args에 있는 게 하나씩 지정되면서 log(arg)를 출력하게 되는 것.

 

 

 

-  더 간단한 방법으로는 forEach라는 함수형 언어를 이용해 출력가능하다. 더 자세한 건 배열공부하기

args.forEach((arg) => console.log(arg));

 

 


5. Local Scope (지역변수)

-  원리: 밖에서는 안이 보이지 않고 안에서만 밖을 볼 수 있다.

// 5. Local scope
let globalMessage = 'global'; // global variable
function printMessage() {
    let message = 'hello';
    console.log(message); // local variable
    console.log(globalMessage);
}
printMessage();

scope에는 2가지가 있다.

1. Global scope

2. Local scope 

 

 

: 블럭{  } 안에서 변수 선언하는 것 = local varialbe (지역변수)
블럭 {  }안에서만 접근 가능
:  {  }블럭 밖에서 밖에서 메시지를 출력하게 되면 에러발생.

ex)

-  console.log(message); 출력시 우측 콘솔에서 에러 발생.

 

 

printMessage() 라는 함수 안에 printAnother() 이라는 함수가 또 있다.

자식부모에게서 정의된 메시지 확인할 수 있다.

-  하지만 자식 안에 정의된 childMessage를 부모에게서 보려고 한다면 에러가 날 것.

:  자식의 함수가 부모함수에 정의된 변수들로 접근 가능하다.

 

 


6. Return a value

: return:  어떤 함수를 사용하고나서 return을 쓰면 그자리에 return에 할당한 값을 그대로 출력하고 해당 함수를 종료한다.

// 6. Return a value
function sum(a, b) {
    return a + b;
}
const result = sum(1, 2); //3
console.log(`sum: ${sum(1, 2)}`);

:  sum이라는 함수 호출 시 1과 2를 합한 값인 3이 return됨

-  return타입이 없는 함수들은 return undefined이 들어있는 것과 똑같다.

 


7. Early return, early exit

// 7. Early return, early exit
// bad
function upgradeUser(user) {
    if (user.point > 10) {
        //long upgrade logic...
    }
}

-  upgradeUser함수 안에서 user.point가 10이상일 때 진행하는 로직이 있다고 한다면 if 블럭 {  }안에서 로직 작성시 가독성이 떨어진다. 이럴 경우 if와 else를 번갈아서 쓰는 것 보다는

 

//good
function upgreadeUser(user) {
    if (user.point <= 10) {
        return;
    }
     // long upgrade logic...
}

 -  조건이 맞지 않을 땐 빨리 return해서 함수를 종료하고 조건 맞을때만 그 다음에 필요한 logic을 작성하는 게 좋다.

 

-  조건이 맞지 않은 경우 / 값이 undefined인 경우 / 값이 -1인 경우 빨리 return하기.

 

 

 


Function Expression (함수 표현)

// First-class function
// functions are treated like any other variable
// can be assigned as a value to variable
// can be passed as an argument to other functions.
// can be returned by another function

<복습>

- first-class function은 다른 함수와 마찬가지로

 변수에 할당이 되고

function의 parameter로 전달

return값으로도 return된다.

 


1. Function expression (함수표현)

// 1. Function expression
// a function declaration can be called earlier than it is defined. (hoisted)
// a function expression is created when the execution reaches it.
const print = function () {
    console.log('print');
};
print();
const printAgain = print;
printAgain();
const sumAgain = sum;
console.log(sumAgain(1, 3));

-  function() 에 이름이 없고, function을 이용해 parameter와 { }블럭 이용한 것을 볼 수 있다. 

함수에 이름이 없는 것을 anonymous function이라고한다.

-  함수의 이름 없이 필요한 부분( console.log('print'); )만 작성해서 할당할 수 있고

함수 이름을 작성할 수도 있다. = named function

ex)

const print = function print() { }

 

 

 

-  함수를 print에 할당하면 print라는 변수에 함수를 호출하듯 (); 로 호출하면 바로 프린트 출력됨.

 

 

 

-다시 다른 변수 (const printAgain)에 할당하게 되면

printAgain은 function을 가리키고 있으므로

printAgain(); 의 값은 print로 출력되는 것을 알 수 있다.

 

 

- (아까 위에서 sum이라는 함수 만들었음)

이것도 sumAgain이라는 변수를 할당하게 되면 동일하게 호출할 수 있다.

 

 

📍Function declaration 과 Function expression 차이점

- function expression 

:  할당된 다음 부터 호출 가능 

 

-  Function declaration

:  hoisting된다. (function이 선언되기 이전에 호출해도 뒤에서 sum이라는 함수를 정의하기 전에 JS엔진이 선언된 것을 제일 위로 올려주기 때문이다.

 

 

 

 


2. Callback function using function expreession

*  Callback(콜백):다른 코드의 인수로서 넘겨주는 실행 가능한 코드(함수로 생각하면 쉽다!)를 의미한다.
즉, 인수로 넘겨주는 코드를 Callback이라고 부른다!

📍callback function이란 무엇일까?
:  다른 코드(함수)의 인수로서 넘겨주는 함수를 의미!

-  사용하는 이유: 프로그램이 비순차적으로 실행됐을 때 사용

// 2. Callback function using function expreession
function randomQuiz(answer, printYes, printNo) {
    if (answer === 'love you') {
        printYes();
    } else {
        printNo();
    }
}

- answer : 정답

- printYes : 정답일 때

- printNo : 정답이 아닐 때

상황에 맞을 때 전달된 함수(printYes, printNo) 를 부르는 것 = callback function

즉, 두 가지의 callback functions가 parameter로 전달돼서 answer이 love you인 경우에만 

printYes라는 콜백함수를 호출, 아닐경우 printNo라는 콜백함수 호출함.

 

 

 

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

const printNo = functino print() {
    console.log('no!');
};
randomQuiz('wrong', printYes, printNo);
randomQuiz('love you', printYes, printNo);

- printYes라는 변수에 yes를 출력하는 함수(function())를 할당하고

printNo라는 변수에는 no를 출력하는 함수((function print())가 온다.

- randomQuiz호출할 때 차례대로 정답, yes콜백함수, no콜백함수 출력.

 

 

-  이름 없는 함수 = anonymous function

// anonymous function
const printYes = function() {
    console.log('yes!');
};

 

- named function

// named function
// better debuffing in debugger's stack traces
// recursions
const printNo = function print() {
    console.log('no!');
};

:  함수의 이름: print

 

📍이름 쓰는 이유

:  디버깅할때 쓰임

:  함수 안에서 또 다른 함수를 호출할 때 쓰임( =recursions),

:  반복되는 평균값 계산 등 쓰임새가 다양하다.

===>알아만두기

 

 

-함수를 무한대로 호출하면 call stack이 꽉 찼다는 에러가 발생한다.

===>알아만두기

 

 


Arrow Function

: 함수를 간결하게 만들어준다.
: 항상 이름이 없는 anonymous function.

// Arrow function
// always anonymous
const simplePrint = function () {
    console.log('simplePrint!');
};

- 문제점: function () { } 괄호 중괄호를 반복적으로 써줘야한다.

 

 

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

- 저 위에 코드를 이렇게 줄일 수 있음.

 

 

또 다른 예시를 보자

// 1번
const add = (a, b) => a + b;

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

1번과 2번 중 어떤 것이 더 간결해 보이는가?

1번이다.

 

 

복잡한 코드를 만들 때 블럭이 필요하다면, 이런식으로 블럭을 넣을 수가 있다.

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

- 블럭 쓰면 return키워드 써서 return 해야한다.

 

 


 

IIFE

: 함수 선언하면 나중에 호출을 해줘야한다. 선언과 동시에 호출할 수 있는 방법.

:  최근에는 잘 쓰이지는 않음.

// IIFE: Immediately Invoked Function Expression
function hello(){
    console.log('IIFE');
}