일련의 과정을 문(statement)으로 구현하고 코드 블록으로 감싸서 하나의 실행 단위로 정의한 것이다.
함수 구성 요소
// 함수 정의와 호출 예시
function add(x, y) { // -> 정의
return x + y;
}
add(2, 5); // 7 -> 호출
function add(x, y) {
// function: 함수 선언 키워드
// add: 함수 이름
// (x, y): 매개 변수 목록
return x + y;
// x + y: 함수의 반환값, 이때 `return 반환값` 형태로 사용해주어야함.
}
add(2, 5); // 7
// 2, 5: 함수의 인자
함수는 객체이다. 함수 리터럴은 function 키워드, 함수 이름, 매게 변수 목록, 함수 몸체로 구성한다.
👉 일반 객체는 호출 할 수 없지만 함수는 호출할 수 있다.
const add = function (x, y) {
return x + y;
};
add(2, 5); // 7
함수를 호출하기 이전에 인수를 전달받을 매개변수와 실행할 문들, 그리고 반환값을 지정함
function add = function (x, y) {
return x + y;
};
const add = function (x, y) {
return x + y;
};
const add = new Function (x, y), return x + y;};
const add =(x, y) => x + y;
함수 선언문은 함수 이름을 생략할 수 없다. <-> 함수 리터럴은 함수이름 생략 가능
<function add(x, y) {
return x + y;
}; // function (x, y) -> 이런식으로 생략할 수 없음
//함수 참조
// console.dir은 console.log와는 달리 함수 객체의 프로퍼티까지 출력함.
console.dir(add)
console.log(add(2,5)); // 7
자바스크립트의 함수는 일급 객체다.
📄 일급객체란? 값처럼 변수에 할당 할 수도 있고, 프로퍼티 값이 될 수도 있고, 배열의 요소도 될수 있다 이처럼 값의 성질을 갖는 객체를 말함
// 가명 함수 표현식
const add = function add(x, y) {
return x + y;
};
//함수 객체를 가르키는 식별자로 호출
console.dir(add(2,5)); // 7
console.log(foo(2,5)); // foo is not defined 에러남...
함수 표현식으로 정의한 함수는 함수 표현식 이전에 호출할 수 없다.
함수 선언문으로 정의한 함수와 함수 표현식으로 정의한 함수의 생성 시점이 다르기 때문이다.
👉 코드가 한 줄씩 순차적으로 실행되는 시점(런타임) 이전에 함수 객체가 먼저 생성되고 자바스크립트
엔진이 함수이름과 동일한 이름의 식별자를 암묵적으로 생성하고 생성된 함수 객체를 할당함
런타임에는 이미 함수 객체 먼저 생성되어 있고 식별자 할당도 완료된 상태가
따라서 함수 선언문 이전에 함수를 참조할 수 있으며 호출할 수도 있다.(호이스팅)
변수 호이스팅: var 키워드를 사용하면 선언된 변수는 undefined로 초기화됨 var 키워드를 사용한 변수 선언문 이전 변수를 참조하면 변수 호이스팅에 의해 undefined로 평가됨
함수 호이스팅: 함수 선언문으로 정의한 함수를 함수 선언문 이전에 호출하면 함수 호이스팅에 의해 호출이 가능하다
👉 함수 표현식으로 함수를 정의하면 함수 호이스팅이 발생하는 것이 아니라 변수 호이스팅이 발생한다.
📄호이스팅이란? 함수 선언문이 코드의 선두로 끌어 올려진 것처럼 동작하는 자바스크립트 고유의 특징
// 함수 참조
console.dir(add); // f add(x,y)
console.dir(sub); // undefined
// 함수 호출
console.log(add(2, 5)); // 7
console.log(sub(2, 5)); // TypeError: sub is not a function
// 함수 선언문
function add(x, y) {
return x + y;
}
// 함수 표현식
const sub = function (x, y) {
return x - y;
};
자바스크립트 기본 제공 빌트인 함수 Function 생성자 함수에 매개변수 목록, 함수 몸체, 문자열 전달하면서 new 연산자와 함께 호출하고 객체를 생성 반환함
var add = new Function("x", "y", "return x + y");
console.log(add(2,5));//7
ES6에서 도입된 화살표(arrow function)은 function 키워드 대신 화살표 ( => ) 를 사용해 더 간략한 방법으로 함수를 선언함.
👉 중괄호( {} )를 사용하지 않으면 return 작성하지않아도 됨, 만약 중괄호( {} )을 사용하면 return 을 명시하는 것이 좋다.
var add = (x, y) => x + y;
함수의 외부에서 내부로 전달할 필요가 있는 경우 매개 변수(인자)를 통해 인수를 전달한다.
- 인수 : 값으로 평가 될 수 있는 표현식, 함수 호출시 지정, 개수,타입 제한 없음
function add(x, y) {
console.log(x, y); // 2, 5
return x + y;
}
// 함수 호출
var result = add(1, 2); // 인수 1,2 매개변수 x,y 순서대로 할당 함수 몸체 문들이 실행됨
console.log(x, y); // Uncaught ReferenceError: x is not defined
👉 매개변수의 스코프는 함수 스코프로, 함수 바깥에서 x, y 에 접근하면 Uncaught ReferenceError 에러가 발생한다.
매개변수는 함수 외부에서 접근이 불가능함 연산 결과를 return해서 전달해야 함수 외부에서 변수에 결과를 저장할 수 있다.
function add(x, y) {
return x + y;
}
let result = add(2); // NaN
let result = add(1, 2); // 3
let result = add(1, 2, 3); // 3
👉 초과된 인수는 버려지는것이 X, 모든 인수는 암묵적으로 arguments 객체의 프로퍼티로 보관됨.
function add(x, y) {
return x + y;
}
// 문법상의 문제 없으므로 실행된다.
console.log(add(2)) //NaN
console.log(add("a", "b")); // "ab"
👉 따라서, 자바스크립트의 경우 함수를 정의할 때 적절한 인수가 전달 되었는지 확인이 필요함
function add(x, y) {
if (typeof x !== "number" || typeof y !== "number")
throw new TypeError("인수는 모두 숫자 값이어야 합니다.");
}
return x + y; // 1 + undefined
}
console.log(add(2)); // TypeError : 인수는 모두 숫자 값이어야 합니다.
console.log(add('a', 'b'); // TypeError : 인수는 모두 숫자 값이어야 합니다.
👉 또한, 함수 호출 시 매개변수 개수를 부족하게 전달할 시 단축 평가를 활용해 기본 값을 할당할 수 있다.
function add(a, b, c) {
a == a || 0; // a 가 truthy 값이면 바로 a 할당.
b == b || 0;
c == c || 0;
return a + b + c;
}
console.log(add(1, 2, 3)); //6
console.log(add(1, 2);//3
console.log(add(1)); //1
console.log(add()); //0
함수는 한 가지 일만 해야하며 가급적 작게 만들어야 한다. ( 많으면 유지 보수 차원에서 힘들다.)
반환문을 사용해 실행 결과를 함수 외부로 반환 할 수 있다.
function multiply(x, y) {
return x * y; // 반환문
}
let result = multiply(3,5);
console.log(result); // 15
반환문의 역활
함수 호출 시 인수가 매개변수에 전달 되는 과정을 말한다.
function changeVal(primitive, obj) {
primitive += 100;
obj.name = "Kim";
}
var num = 100;
var person = { name: "Lee" };
console.log(num); // 100
console.log(num); // {name: "Lee"};
// 원시 값은 값 자체가 복사되어 전달됨 객체는 참조 값이 복사되어 전달
changeVal(num, person);
console.log(num); // 100
console.log(person); // {name:"Kim"};
// 원시 값은 원본이 회손되지 않는다.
console.log(person); // {num}; //100
console.log(person); // {person}; // {name : "kim"}
❗하지만 객체가 변경할 수 있는 값이며, 참조에 의한 전달 방식으로 동작하기 때문에 부작용이 있다.
부작용 방지 방법
// 함수 정의와 동시에 호출 되는 함수를 의미함
// 익명 즉시 실행 함수
(function foo() {
let a = 3;
let b = 5;
return a * b;
}());
(function foo() {
let a = 3;
let b = 5;
return a * b;
}());
foo(); // ReferenceError : foo is not defined
let result = (function foo() {
let a = 3;
let b = 5;
return a * b;
}());
console.log(result); // 15
즉시 실행 함수도 일반 함수처럼 값을 반환할 수 있다. 함수 스코프 내에서만 변수가 활용되기 때문에 전역변수와의 이름 중복을 해결할 수 있다.
또한 익명함수기 때문에 함수 이름 중복을 해결할 수 있다.
함수가 자기 자신을 다시 호출하는 함수를 말한다.
function countDown(n) {
for (let i = n; i >= 0; i--) console.log(i);
}
countDown(10);
function countdown(n) {
if (n < 0) return;
console.log(n);
return countdown(n - 1); //재귀 호출
}
countDown(10);
위 예시에서, 반복문을 사용한 countDown()
과 재귀함수 countdown()
은 동일한 기능을 수행한다.
이렇게 반복적인 작업을 대체하는 역할을 수행할 수 있다.
재귀 함수의 대표적인 예시가 팩토리얼을 구하는 함수다.
function factorial(n) {
if (n <= 1) return 1;
// 재귀 호출
return n * factorial(n - 1);
console.log(factorial(0)) // 0! = 1
console.log(factorial(1)) // 1! = 1
console.log(factorial(2)) // 2! = 2 * 1 =2
console.log(factorial(2)) // 3! = 3* 2 * 1 = 6
// n! = 1 * 2 ..... * (n-1) *n -> 탈출 조건: n이 1이하 일때 재귀 호출을 멈춤
}
❗ 재귀함수 사용 주의점
함수 내부에 정의한 함수를 칭한다. 중첩 함수 또는 내부 함수라고 한다. -> 자신을 포함하는 외부 함수를 돕는 헬퍼 함수이다.
function outer() {
let x = 1;
function inner() {
let y = 2;
console.log(x + y);
}
inner();
}
outer();
// `inner()` 에서는 `outer()` 의 변수에 접근이 가능
함수의 매개변수로 전달되는 함수를 말한다.
function repeat(n) {
for (let i = 0; i < n; i++) {
console.log(i) -> 이때 repeat 함수는 console.log(i)에 강하게 의존하여 다른일을 할수 없다.
}
}
repeat(5); // 0 1 2 3 4
// 다른일을 하도록 함수를 새롭게 정의
function repeat(n, f) {
for (var i = 0; i < n; i++) {
f(i); // i를 전달하면서 f를 호출
}
}
var logAll = function (i) {
console.log(i);
};
repeat(5, logAll);
repeat 는 함수 logAll 을 전달 받아 수행한다.
logAll
-> 콜백함수 repeat
-> 고차함수 라고 한다. repeat
로 전달된 logAll
은 repeat
내에서 스스로 실행시점을 결정하지 못하고 고차 함수에서 정해진 순서에 따라 실행된다.
콜백함수가 여러 곳에서 호출된다면, 전역 공간에 선언하거나 익명함수로 선언해 다른 곳에 사용되지 않도록 하는게 좋다.
순수 함수는 어떤 외부 상태에 의존하지 않고, 변경하지도 않는 함수를 말한다. 즉, 부수 효과가 없는 함수를 순수 합수라 한다.
비순수 함수는 그와 반대로 외부 상태에 의존하며, 변경하는 함수를 말한다. -> 부수 효과가 있음
👉 순수 함수는 매개변수를 통해 함수 내부로 전달된 인수만 사용해 값을 생성 후 반환한다.
📄 부수 효과란? 함수가 수행해야 하는 본연의 일 이외에 부수적으로 수행하는 일을 의미함.
let countn = 0; // 현재 카운트를 나타내는 상태
// 순수 함수 increase는 동일한 인수가 전달되면 언제나 동일 값을 반환
increase(n){
return ++n;
}
// 순수 함수가 반환한 결과값을 변수에 재할당해서 상태를 변경
counst = increase(count);
console.log(counst); // 1
counst = increase(count);
console.log(counst); // 2
let countn = 0; // 현재 카운트를 나타내는 상태
// 비순수 함수
increase(){
return ++countn; // 외부 상태에 의존, 외부 상태 변경 시킴
}
// 비순수 함수는 외부 상태(count)를 변경하므로 상태 변화를 추적하기 어려워진다.
increase();
console.log(countn); // 1
increase();
console.log(countn); // 2