// 함수를 만드는 방법
// 1. 함수 선언(Function Declaration)
function 함수이름(파라미터) {
동작
return 리턴값;
}
// 2. 함수 표현식(Function Expression)
const printCracked = function () {
console.log('Cracked');
};
printCracked(); // Cracked
// 또는
const myBtn = document.querySelector('#myBtn');
myBtn.addEventListener('click', function() {
console.log('button is clicked!');
});
// ----------------------------------------------
// 함수 선언식과 함수 표현식의 차이1
// 호이스팅
printCracked();
function printCracked() {
console.log('Cracked'); // Cracked
}
// 또는
printCracked(); // ReferenceError
const printCracked = function() {
console.log('Cracked');
}
// ----------------------------------------------
// 함수 선언식과 함수 표현식의 차이2
// 스코프
function printCracked() {
function printJS() {
console.log('JavaScript');
}
console.log('Cracked');
printJS();
}
printCracked();
printJs(); // ReferenceError
// 또는
const x = 4;
if (x < 5) {
function printJS() {
console.log('JavaScript');
}
}
{
function printCracked() {
console.log('Cracked');
}
}
printCracked(); // Cracked
printJS(); // JavaScript
// ----------------------------------------------
var printJS = function () {
console.log('JavaScript');
};
let printHi = function () {
console.log('Hi');
};
const printCracked = function () {
console.log('Cracked');
};
함수 표현식으로 함수를 만들 때는 선언하는 함수에 이름을 붙여줄 수도 있다. 이름이 있는 함수 표현식, 즉 기명 함수 표현식이라고 부른다. 함수 표현식으로 함수가 할당된 변수에는 자동으로 name이라는 프로퍼티를 가지게 된다.
const sayHi = function printHiInConsole() {
console.log('Hi');
};
console.log(sayHi.name); // printHiInConsole
printHiInConsole(); // ReferenceError
만약 함수에 일므이 없으면 할당할 때는 변수의 name 프로퍼티는 변수 이름 그 자체를 문자열로 가지게 된다. 위의 예시처럼 함수에 이름을 붙여주게 되면, name 속성은 함수 이름을 문자열로 갖게 된다.
함수 이름은 함수 내부에서 함수 자체를 가리킬 때 사용할 수 있고 함수를 외부에서 함수를 호출할 때 사용할 수는 없다.
let countdown = function(n) {
console.log(n);
if (n === 0) {
console.log('End!');
} else {
countdown(n - 1);
}
};
countdown(5);
아규먼트로 숫자 값을 전달하고 전달받은 그 값이 0일 될 때까지 하나씩 값을 줄이면서 자기 자신을 호출하는 countdown이라는 함수를 함수 표현식으로 작성해 보았다. 이런 식으로 자기 자신을 부르는 함수를 재귀 함수(Recursive function)라고 부른다. 그런데 만약 이 함수를 복사하려고 다른 변수에 똑같이 담았다가, countdown 변수에 담긴 값이 변하게 되면 문제가 발생한다.
let countdown = function(n) {
console.log(n);
if (n === 0) {
console.log('End!');
} else {
countdown(n - 1);
}
};
let myFunction = countdown;
countdown = null;
myFunction(5); // TypeError
마지막 줄에서 myFunction 함수를 호출했을 때, 함수가 실행되긴 하지만, 6번줄 동작을 수행할 때 호출하려는 countdown 함수가 이미 12번에서 null 값으로 변경되었기 때문에 함수가 아니라는 TypeError가 발생한 것이다. 이런 상황을 방지하기 위해서 함수 내부에서 함수 자신을 사용하려고 하면 함수 표현식에서는 반드시 기명 함수 표현식을 사용하는 것이 좋다.
let countdown = function printCountdown(n) {
console.log(n);
if (n === 0) {
console.log('End!');
} else {
printCountdown(n - 1);
}
};
let myFunction = countdown;
countdown = null;
myFunction(5); // 정상적으로 동작
(function (x, y) {
console.log(x + y);
})(3, 5);
함수선언 부분을 소괄호로 감싼 다음에 바로 뒤에 함수를 실행하는 소괄호를 한 번 더 붙여주는 방식이다. 이렇게 하면 함수가 선언된 순간 바로 실행이 된다. 이렇게 함수 선언과 동시에 즉시 실행되는 함수를 가리켜 즉시 실행 함수 (표현)이라고 부른다. (IIFE, Immediately Invoked Function Expression) 일반 함수처럼 파라미터를 작성하고, 함수를 호출할 때 아규먼트를 전달할 수도 있다.
(function sayHi() {
console.log('Hi!');
})();
sayHi(); // ReferenceError
// ------------------------------------
(function countdown(n) {
console.log(n);
if (n === 0) {
console.log('End!');
} else {
countdown(n - 1);
}
})(5);
즉시 실행 함수는 함수에 이름을 지어주더라도 외부에서 재사용할 수 없다. 그래서 일반적으로는 이름이 없는 익명 함수를 사용한다. 다만, 함수 내부에서 자기 자신을 호출하는 재귀적인 구조를 만들고자 할 땐 이름이 필요할 수도 있다는 점도 알아두면 좋다.
즉시 실행 함수는 말 그대로 선언과 동시에 실행이 이뤄지기 때문에 프로그램 초기화 기능에 많이 활용된다.
// 일회성 동작
(function init() {
// 프로그램이 실행 될 때 기본적으로 동작할 코드들..
})();
// 리턴값 바로 변수에 할당
const firstName = 'Young';
const lastName = 'Kang';
const greetingMessage = (function () {
const fullName = `${firstName} ${lastName} `;
return `Hi! My name is ${fullName}`;
})();
// 다른 함수에 파라미터로 전달된 함수는 콜백 함수(CallBack Function)이라고 한다.
function makeQuiz(quiz, answer, success, fail) {
if (prompt(quiz) === answer) {
console.log(success());
} else {
console.log(fail());
}
};
function getSuccess() {
return '정답';
};
function getFail() {
return '오답!';
};
const question = '5 + 3 = ?';
makeQuiz(question, '8', getSuccess, getFail);
// 함수를 리턴하는 함수를 고차 함수(Higher Order Function)라고 한다.
function getPrintHi() {
return function () {
console.log('Hi!?');
};
};
const sayHi = getPrintHi();
sayHi();
getPrintHi()(); // 고차 함수를 바로 리턴
프로그래밍에서 콜백 함수나 고차 함수 등을 가리켜 일급 함수(First Class Function)이라고 부른다.
// 변수에 할당해서 활용
const printJS = function () {
console.log('JavaScript');
};
// 객체의 메소드로 활용
const Cracked_Egg = {
printTitle: function () {
console.log('Cracked_Egg');
}
}
// 콜백 함수로 활용
myBtn.addEventListener('click', function () {
console.log('button is clicked!');
});
// 고차 함수로 활용
function myFunction() {
return function () {
console.log('Hi!?');
};
};
// Parameter
function greeting(name) { // 파라미터(Parameter)
console.log(`Hi! My name is ${name}!`);
};
greeting('JavaScript'); // 아규먼트(Argument)
greeting(); // undefined
// ----------------------------- //
function greeting(name = 'Cracked_Egg', interest = 'JavaScript') {
console.log(`Hi! My name is ${name}!`);
console.log(`I like ${interest}!`);
}
greeting(undefined, 'Python');
// Hi! My name is Cracked_Egg
// I like Python
// ----------------------------- //
function defaultTest(x, y = x + 3) {
console.log(`x: ${x}`);
console.log(`y: ${y}`);
};
defaultTest(2);
// x: 2
// y: 5
// arguments
function printArguments(a, b, c) {
console.log(a);
console.log(b);
console.log(c);
};
printArguments('A'); // A undefined undefined
printArguments('A', 'B', 'C', 'D'); // A B C
// arguments는 유사 배열이다.
// arguments.length
// arguments[0]
// for (const org of arguments)
arguments 객체는 함수를 호출할 때 전달한 아규먼트들을 배열의 형태로 모아둔 유사 배열 객체이다. 특히, 함수를 호출할 때 전달되는 아규먼트의 개수가 불규칙적일 때 유용하게 활용될 수 있다.
// Rest Parameter는 배열이다.
function printArguments(...args) {
for (const org of args) {
console.log(arg);
};
};
printArguments('A', 'B', 'C'); // A, B, C
// -------------------- //
function printArguments(...args) {
console.log(args.splice(0, 2));
};
printArguments('A', 'B', 'C'); // ['A', 'B']
// -------------------- //
function printRank(first, second, ...others) {
console.log(`우승: ${first}`);
console.log(`준우승: ${second}`);
for (const org of others) {
console.log(`참가자: ${arg}`);
}
};
printRank('A', 'B', 'C', 'D', 'E');
// 우승: A
// 준우승: B
// 참가자: C
// 참가자: D
// 참가자: E
파라미터 앞에 마침표 세 개를 붙여주면, 여러 개로 전달되는 아규먼트들을 배열로 다룰 수가 있게 된다. rest parameter는 배열이기 때문에 배열의 메소드를 자유롭게 사용할 수 있다는 장점이 있다.
// Arrow Function
const getTwice = (number) => {
return number * 2;
};
console.log(getTwice(5)); // 10
// --------------------------- //
const myBtn = document.querySelector('#myBtn');
myBtn.addEventListener('click', () => {
console.log('button is clicked!');
}); // button is clicked!
// --------------------------- //
// 파라미터가 하나인 함수는 소괄호를 생략할 수 있다.
// 함수 내부에 문장이 하나이면 중괄호도 생략이 가능하다.
// 함수 내부에 동작이 객체인 경우, 소괄호를 감싸서 사용이 가능하다.
const getTwice = number => number * 2;
const getName = () => ({ name: 'Cracked_Egg', });
console.log(getTwice(6)); // 12
console.log(getName()); // {name: "Cracked_Egg"}
arrow function은 익명 함수를 좀 더 간결하게 표현할 수 있도록 ES2015에서 새롭게 등장한 함수 선언 방식이다. 다양한 상황에 따라 축약형으로 작성될 수도 있다.
// 1. 함수의 파라미터가 하나 뿐일 때
const getTwice = (number) => {
return number * 2;
};
// 파라미터를 감싸는 소괄호 생략 가능
const getTwice = number => {
return number * 2;
};
// 2. 함수 동작 부분이 return문만 있을 때
const sum = (a, b) => {
return a + b;
};
// return문과 중괄호 생략 가능
const sum = (a, b) => a + b;
arrow function과 일반 함수에는 몇 가지 차이점이 있는데, 가장 대표적인 차이점은 arguments 객체가 없고, this가 가리키는 값이 일반 함수와 다르다는 점이다.
// this => 함수를 호출한 객체를 가리킨다
function getFullName() {
return `${this.firstName} ${this.lastName}`;
};
const user = {
firstName: 'Cracked',
lastName: 'Egg',
getFullName: getFullName,
};
const admin = {
firstName: 'Hi',
lastName: 'Hello',
getFullName: getFullName,
};
console.log(user.getFullName());
console.log(admin.getFullName());
웹 브라우저에서 this가 사용될 때는 전역 객체, Window 객체를 가지게 된다. 하지만 객체의 메소드를 정의하기 위한 함수 안에선 메소드를 호출한 객체를 가리키게 된다.