함수란?
JavaScript에서 반복문, 조건문처럼 가장 중요한 핵심 개념이다.
스코프(Scope), 실행 컨텍스트, 클로저(Closure), 생성자 함수에 의한 객체 생성, 메서드(Method),
this, 프로토타입(Prototype), 모듈화 등
모두 함수와 깊은 관련이 있다.
함수의 구성요건
// 함수 리터럴 구성요소
// 함수명
- 함수명은 식별자다. 따라서 식별자 네이밍 규칙을 준수해야한다.
- 함수명은 함수 몸체 내에서만 참조할 수 있는 식별자이다.
- 함수명은 생략할 수 있다. (기명함수 : 이름이 있는 함수 / 익명함수 : 이름이 없는 함수)
// 매개변수 (parameter)
- 0개 이상의 매개변수를 소괄호로 감싸고 쉼표로 구분한다.
- 각 매개변수에는 함수를 호출할 때 지정한 전달인자(인수)가 순서대로 할당된다.
(즉, 매개변수 목록은 순서에 의미가 있다.)
- 매개변수는 함수 몸체 내에서 변수와 동일하게 취급된다.
따라서 매개변수도 변수와 마찬가지로 식별자 네이밍 규칙을 준수해야한다.
// 함수몸체 (body)
- 함수가 호출되었을 때 일괄적으로 실행될 문들을 하나의 실행 당위로 정의한 코드블록이다.
- 함수 몸체는 함수 호출에 의해 실행된다.
// 함수를 변수에 할당
let func = function add(x, y) {
return x + y;
};
함수 리터럴을 변수에 할당하고 있다.
함수 리터럴도 값을 생성하며, 이 값은 객체이다.
즉, 함수는 객체이다.
하지만 함수는 일반 객체와 다르다.
일반 객체는 호출할 수 없지만 함수는 호출할 수 있다.
그리고 일단 객체에 없는 함수 객체만의 고유한 프로퍼티를 갖는다.
함수의 정의 방식
함수 선언문
(function declaration)
function add(x, y) {
return x + y;
}
console.log(add(3, 4)); // 7
// 함수 선언문은 익명함수 사용이 불가능하다!
function (num1, num2){
return num1 + num2;
} // SyntaxError: Function statements require a function name
// 함수 선언문을 변수에 할당한 경우 = 기명 함수 표현식
// 함수 호출시 함수명으로 호출되는 것이 아니라 함수를 할당한 변수명(식별자)으로 호출된다.
let sum = function add(x, y) {
return x + y;
};
console.log(sum(3, 4)); // 7
console.log(add(3, 4)); // ReferenceError: add is not defined
함수 표현식
(function expression)
// 함수 표현식은 함수명을 생략하는 것이 일반적이다.
let add = function (x, y) {
return x + y;
};
console.log(add(3, 4)); // 7
// 기명 함수 표현식 = 함수 선언문을 변수에 할당한 경우
// 함수 호출시 함수명으로 호출되는 것이 아니라 함수를 할당한 변수명(식별자)으로 호출된다.
let sum = function add(x, y) {
return x + y;
};
console.log(sum(3, 4)); // 7
console.log(add(3, 4)); // ReferenceError: add is not defined
화살표 함수
(arrow function)
// 화살표 함수는 ES6에서 도입되었다.
// 화살표 함수는 항상 익명함수로 정의한다.
// function 대신 =>를 사용한다.
// 화살표함수는 기존의 함수보다 표현만 간략한 것이 아니라 내부 동작 또한 간략화 되어 있다.
// 생성자 함수로 사용할 수 없다.
// 기존 함수와 this 바인딩 방식이 다르다.
// prototype(프로토타입) 프로퍼티가 없다.
// arguments 객체를 생성하지 않는다.
let add = (x, y) => x + y;
console.log(add(3, 4)); // 7
Function 생성자 함수
(constructor function)
// function 생성자 함수에 매개변수 목록과 함수 몸체를 문자열로 전달하면서
// new 연산자와 함께 호출하면 함수 객체를 생성해서 반환한다.
// new 연산자 없이 호출해도 결과는 동일하다.
let add = new Function('x', 'y', 'return x + y');
console.log(add(3, 4)); // 7
// 하지만 function 생성자 함수로 함수를 생성하는 방법은 바람직하지 않고,
// 클로저(Closure)를 생성하지 않으며,
// 함수 선언문이나 함수 표현식으로 생성한 함수와 다르게 동작한다.
// 함수 표현식
var add1 = (function () {
var a = 10;
return function (x, y) {
return x + y + a;
};
}());
console.log(add1(1, 2)); // 13
// Function 생성자 함수
var add2 = (function() {
var a = 10;
return new Function('x', 'y', 'return x + y + a');
}());
console.log(add2(1, 2)); // ReferenceError: a is not defined
호이스팅(hoisting)
// 함수 참조
console.dir(add(3, 4)); // f add(x, y)
console.dir(sum(3, 4)); // undefined
console.dir(sub(3, 4)); // ReferenceError: Cannot access 'sum2' before initialization
// 함수 호출
console.log(add(3, 4)); // 7
console.log(sum(3, 4)); // TypeError: sum1 is not a function
console.log(sub(3, 4)); // ReferenceError: Cannot access 'sum2' before initialization
// 함수 선언문
function add(x, y){
return x + y;
}
// var 함수 표현식
var sum = function (x, y){
return x + y;
}
// let 함수 표현식
let sub = function (x, y){
return x - y;
}
위 예제와 같이 결과가 나오는 이유는
함수 선언문으로 정의한 함수와 함수 표현식으로 정의한 함수의 생성 시점이 다르기 때문이다.
함수 선언문이 코드의 선두로 끌어 올려진 것처럼
동작하는 특징을 호이스팅(hoisting)이라 한다.
함수 표현식으로 함수를 정의하면 함수 호이스팅이 발생하는 것이 아니라
변수 호이스팅이 발생한다.
쉽게 표현하자면
원래는 위에서부터 아래로 쭉 코드를 읽는데
호이스팅(hoisting)은 코드를 읽는 것을 시작하기 전에 미리 해당 코드를 읽는 행위이다.
그리고
함수 선언식으로 작성한 함수만 호이스팅이 되고,
함수 표현식으로 작성된 함수는 호이스팅이 되지 않는다.
함수 호출
매개변수와 전달인자(인수)
(Parameter & Argument)
함수의 구성요건
함수는 함수를 호출하기 전까지
함수의 코드블록 내부의 코드는 실행되지 않는다.
함수를 실행하기 위해 필요한 값을 함수 외부에서 함수 내부로 전달할 필요가 있는 경우,
전달인자(인수)는 값으로 평가될 수 있는 표현식이어야 하고,
전달인자(인수)는 함수를 호출할 때 지정하며,
개수와 타입에 제한이 없다.
매개변수는 함수를 정의할 때 선언하며,
함수 몸체 내부에서 변수와 동일하게 취급된다.
즉, 함수가 호출되면 함수 몸체 내에서 암묵적으로 매개변수가 생성되고
일반 변수와 마찬가지로 undefined로 초기화된 이후
전달인자(인수)가 순서대로 할당된다.
함수가 호출될 때마다 매개변수는 이와 같은 단계를 거친다.
매개변수는 순서에 의미가 있다.
이상적인 함수는 한 가지 일만 해야하며 가급적 작게 만들어야 한다.
그렇기에 매개변수 수는 적을 수록 좋지만
최대 3개 이상을 넘지 않는 것을 권장한다.
// 매개변수의 수 > 전달인자(인수)의 수
// 전달인자(인수)가 부족하여 할당되지 않은 매개변수의 값은 undefined 이다.
// 하지만 에러는 발생하지 않는다.
// example 1
function add (x, y) {
return x + y;
};
console.log(add(3)); // NaN (= 3 + undefined)
// example 2
function getUserName (user1, user2) {
console.log(user1); // kimcoding
console.log(user2); // undefined
};
getUserName('kimcoding');
// 매개변수의 수 < 전달인자(인수)의 수
// 초과된 전달인자(인수)는 무시된다.
// 하지만 에러는 발생하지 않는다.
// 초과된 전달인자는 그냥 버려지는 것이 아니라,
// 모든 전달인자는 암묵적으로 arguments 객체의 프로퍼티로 보관된다.
function add (x, y) {
return x + y;
};
console.log(add(3, 4, 5)); // 7
// 매개변수에 기본값을 할당한 경우
function add (a, b, c = 3){
return a + b + c;
}
console.log(add()); // NaN (= undefined + undefined + 3)
console.log(add(1)); // NaN (= 1 + undefined + 3)
console.log(add(1, 2)); // 6 (= 1 + 2 + 3)
console.log(add(1, 2, 5)); // 8 (= 1 + 2 + 5)
// 스코프(Scope) (스코프 단원에서 자세히 알아봅시다.)
// 매개변수와 함수 내부에서 선언한 변수는 함수 몸체 내부에서만 참조할 수 있고,
// 함수 몸체 외부에서는 참조할 수 없다.
function add(x, y) {
console.log(x, y); // 3 4
return x + y;
}
add(3, 4);
console.log(x, y); // ReferenceError: x is not defined
반환문 (return문)
// 함수 내부 코드가 차례대로 실행되다가 반환문을 만나면 값을 반환한 후 함수 몸체를 빠져나간다.
// (= return 문 뒤에 나오는 코드는 실행되지 않는다.)
function add (x, y) {
return x + y; // 반환문
// 반환문 이후에 다른 문이 존재하면 그 문은 실행되지 않고 무시된다.
console.log('실행되지 않습니다');
}
console.log(add(3, 4)); // 7
// 반환문은 return 키워드 뒤에 오는 표현식을 평가해 반환한다.
// return 키워드 뒤에 반환값으로 사용할 표현식을 명시적으로 지정하지 않으면 undefined가 반환된다.
function foo () {
return;
}
console.log(foo()); // undefined
// 반환문 생략
// 반환문을 생략하면 함수는 함수 몸체 마지막 문까지 실행한 후 암묵적으로 undefined를 반환한다.
function foo() {
//return문 생략
}
console.log(foo()); // undefined
<!-- 반환문은 함수 몸체 내부에서만 사용할 수 있다. -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
return; // SyntaxError: Illegal return statement
</script>
</body>
</html>
// 함수 호출의 결과를 변수에 할당할 수 있다.
function add (x, y) {
return x + y; // 반환문
}
let result = add(3, 4);
console.log(result); // 7
// 함수의 호출 결과끼리의 연산할 수 있다.
function add (x, y) {
return x + y; // 반환문
}
let result = add(3, 4) + add(5, 7);
console.log(result); // 19
Function (1)을 알아보는 시간이었습니다.
틀린 내용은 댓글로 알려주시면 감사하겠습니다.