function name(parameter1, parameter2) {
body 작성
return;
}
function printHello() {
console.log("Hello");
}
printHello();
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를 제공해야할 경우
메모리에 값이 저장되어있으므로 값 자체를 전달
메모리에 참조 값(reference)이 저장되어있으므로 참조 값 전달
function changeName(object) {
object.name = "coder";
}
const elena = { name: "elena" };
changeName(elena);
console.log(elena); // { name: "coder" }
함수 호출 시 필요한 인자를 넣지 않았을 경우, 기본적으로 출력되는 defalut parameters를 함수 선언 시 설정해놓을 수 있음.
function showMessage(message, from = 'unknown') {
console.log(`${message} by ${from}`);
}
showMessage("Hi"); // Hi from unknown
위와 같이 parameter = 'defalut value'를 설정하는 방식으로 사용자가 값을 전달하지 않았을 때 자동으로 defalut로 설정한 값이 출력되도록 설정할 수 있다.
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 offunction 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");
밖에서는 안이 보이지 않고, 안에서만 밖을 볼 수 있다.
즉, 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라고 함.
함수에서는 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 examplefunction upgradeUser(user) { if (user.point > 10) { // 긴 업그레이드 로직이 들어감 // block 안에서 로직을 많이 작성하면 가독성이 떨어짐 } }
good example
function upgradeUser(user) { if (user.point <= 10) { // 조건이 맞지 않을 때는 빠른 return해서 함수를 종료 return; } // block 밖에서 조건이 맞을 때만 긴 업그레이드 로직을 넣음 }
즉, 함수는 다른 변수와 마찬가지로 변수에 할당이 되고
다른 함수의 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) 사용함
// 함수 표현식 예시
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 키워드를 함께 작성해야한다.
함수를 선언하고 따로 호출해주는 것이 아니라,
선언함과 동시에 바로 호출을 해주는 방법.
(function hello() {
console.log("IIFE");
})(); // IIFE
함수를 바로 호출하고 싶을 때 유용하게 사용될 수 있음.