2023.02.08
자바스크립트 스터디 4회차
공부 사이트: https://poiemaweb.com/
12. 함수
"어떤 작업을 수행하기 위해 필요한 문(statement)들의 집합을 정의한 코드 블록"
자바스크립트의 함수는 일급 객체이다.
#일급 객체: 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체. 변수나 객체, 배열 등에 저장할 수 있고 다른 함수에 인자로서 전달될 수 있으며 함수의 반환값도 될 수 있음.
[일급 객체 특징]
- 무명의 리터럴로 표현 가능
- 변수, 자료구조에 저장 가능
- 함수의 파라미터에 전달 가능
- 반환값으로 사용 가능
1) 함수 정의
- 함수 선언문
- 함수 표현식
- Function 생성자 함수
① 함수 선언문
함수 선언문 방식으로 정의한 함수는 function 키워드와 함수명, 매개변수, 함수 몸체로 구성된다.
function square(num){
return num * num;
}
② 함수 표현식
함수의 일급객체 특성을 이용하여 함수 리터럴 방식으로 함수를 정의, 변수에 할당할 수 있는데 이러한 방식을 함수 표현식이라 한다.
var square = function(num){
return num * num;
}
함수 표현식 방식으로 정의한 함수는 함수명 생략이 가능하다. → 익명 함수(anonymous function)
// 기명 함수 표현식(named function expression)
var square1 = function multiply(num){
return num * num;
}
// 익명 함수 표현식(anonymous function expreesion)
var square2 = function(num){
return num * num;
}
console.log(square1(10,5));
console.log(multiply(2,3)); // ReferenceError. mulyiply is not defined
함수는 일급객체이기에 변수에 할당될 수 있는데, 이 변수는 할당된 함수를 가리키는 참조값을 저장하게 된다.
그래서 함수를 호출할 때는 함수명이 아니라 함수를 가리키는 변수명을 사용해야 한다.
기명 함수 표현식에서의 함수명은 ⓐ함수 몸체에서 재귀적 호출(Recursive function call)을 할 때, ⓑ자바스크립트 디버거가 해당 함수를 구분할 때 사용된다.
사실 알고보면, 함수 선언문도 내부적으로 자바스크립트 엔진이 기명 함수 표현식으로 변환한다.
즉, 함수 선언문도 함수 리터럴 방식을 사용한다는 것.
③ Function 생성자 함수
함수 리터럴 방식으로 함수를 정의하는 것은 내장 함수 Function 생성자 함수로 함수를 생성하는 것을 단순화시킨 축약법(short-hand)이다.
Function 생성자 함수는 Function.prototype.constructor 프로퍼티로 접근할 수 있다.
근데 Function 생성자 함수로 함수 생성은 잘 사용하지 않는 방식임.
// Function 생성자 함수로 함수 생성 Syntax
new Function(arg1, arg2, ..., argN, functionBody)
// Example
var square = new Function('num', 'return num * num');
console.log(square(10));
2) 함수 호이스팅
(참고 - 변수 호이스팅)
함수 호이스팅이란, 함수 선언의 위치와 상관없이 코드 내 어느곳에서든지 호출이 가능하다는 의미이다.
자바스크립트는 ES6의 let, const를 포함하여 모든 선언(var, function, class ...)을 호이스팅한다.
#호이스팅: 모든 선언문이 해당 Scope의 선두로 옮겨진 것처럼 동작하는 특성. 선언되기 이전에 참조 가능함.
함수 선언문으로 정의된 함수는 스크립트가 로딩되는 시점에 바로 초기화되고, 변수 객체에 저장된다. 여기서 함수 선언, 초기화, 할당이 한번에 이루어진다. 그렇기에 함수 선언의 위치와 상관없이 어디에서나 호출이 가능한 것이다.
[TIP] 변수 호이스팅과의 차이
변수 호이스팅은 변수 생성/초기화 단계와 할당 단계가 분리되어 진행된다. 호이스팅된 변수는 undefined로 초기화되고, 실제 값의 할당은 할당문에서 이루어진다.
그러나 함수 선언문으로 정의된 함수는 선언/초기화/할당 단계가 한번에 이루어진다.
그런데 함수 표현식은 다르다.
함수 표현식은 스크립트가 로딩되는 시점에 변수 객체에 함수를 할당하지 않는다. 런타임에 해석되고 실행됨!
더글라스 크락포트 曰
함수 호이스팅은 "함수 호출 전 반드시 함수를 선언해야 한다" 는 규칙을 무시하므로, 코드의 구조를 엉성하게 만들 수 있다. 그렇기에 함수 표현식만을 사용할 것을 권고한다.
게다가 함수 선언문으로 함수를 정의하면 사용은 쉽지만....
대규모 애플리케이션 개발 시 인터프리터가 너무 많은 코드를 변수 객체에 저장하므로, 응답속도가 느려질 수 있다.
3) 일급 객체(First-Class Object)
// 1. 무명의 리터럴로 표현 가능
// 2. 변수, 자료구조에 저장 가능
var increase = function(num){
return ++num;
}
var decrease = function(num){
return --num;
}
var array = { increase, decrease };
console.log(array);
// 3. 함수의 매개변수에 전달 가능
// 4. 반환값으로 사용 가능
function makeCounter(elem){
var num=0;
return function(){
num = elem(num);
return num;
};
}
var increaser = makeCounter(array.increase);
console.log(increaser());
console.log(increaser());
var decreaser = makeCounter(array.decrease);
console.log(decreaser());
console.log(decreaser());
4) 매개변수(Parameter, 인자)
함수의 작업 실행을 위해 추가적인 데이터가 필요한 경우 지정한다.
① 매개변수(parameter, 인자) , 인수(argument)
매개변수(인자)는 함수 내에서 변수와 동일하게 메모리 공간을 확보.
인수는 함수에 전달되어 매개변수에 할당된다. 인수를 전달하지 않으면 매개변수는 undefined로 초기화.
var func1 = function(p1,p2){ // 매개변수(인자) p1과 p2
console.log(p1,p2);
}
func1('Hello'); // p2 매개변수에 인수 미전달
② 값에 의한 호출(Call-by-value)
원시 타입 인수는 값에 의한 호출로 동작한다.
함수 호출 시 원시 타입 인수를 함수에 매개변수로 전달할 때, 매개변수에 값을 복사하여 함수로 전달하는 방식이다.
함수 내에서 매개변수 통해 값이 변경되어도, 전달이 완료된 원시 타입 값은 변경되지 않는다.
function func1(num){
num += 1;
return num;
}
var x = 123;
console.log('func1(x): ' + func1(x));
console.log('x: ' + x); //그대로임
③ 참조에 의한 호출(Call-by-reference)
객체형 인수는 참조에 의한 호출로 동작한다.
함수 호출 시 참조 차팁 인수를 함수에 매개변수로 전달할 때, 매개변수에 값이 복사되지 않고 객체의 참조값이 매개변수에 저장되어 함수로 전달하는 방식이다.
함수 내에서 매개변수 통해 값이 변경되면, 전달된 참조형의 인수값도 같이 변경된다.
function changeFunc(num, obj) {
num += 100;
obj.name = '홍길동';
obj.age = '13';
}
var num = 100;
var person = {
name: '고길동',
age: '53'
};
console.log('num: ' + num);
console.log(person);
changeFunc(num, person);
console.log('num: ' + num);
console.log(person);
[changeFunc 함수]
- 원시 타입 인수와 객체 타입 인수를 전달받아 함수 몸체에서 매개변수 값을 변경
- 이 때, 원시 타입 인수는 값을 복사하여 매개변수에 전달 → 함수 몸체에서 값을 변경해도 그대로임
- 그러나 객체 타입 인수는 참조값을 매개변수에 전달 → 함수 몸체에서 값 변경 시 원본 객체가 변경
이러한 부수 효과를 발생시키는 비순수 함수는 복잡성을 증가시킨다.
#부수효과(side-effect: 값을 반환하는 메소드나 함수가 외부 상태를 변경하는 경우. 의도하지 않은 부수효과는 잠재적인 버그를 많이 발생시킬 수 있다.
- 순수 함수(Pure function): 어떤 외부 상태도 변경하지 않는 함수
- 비순수 함수(Impure function): 외부 상태도 변경시키는 부수 효과를 발생시키는 함수
비순수 함수를 줄일수록 부수 효과도 줄어들기에, 디버깅이 쉬워진다.
5) 반환값
함수는 자신을 호출한 코드에게 수행 결과를 반환할 수 있다.
- return 키워드: 함수를 호출한 코드(caller)에게 값 반환 시 사용
- 함수는 배열 등을 이용하여 한번에 여러 값을 반환할 수 있음
- 반환 생략 가능. 암묵적으로 undefined 반환
- 자바스크립트 해석기는 return 키워드를 만나면 함수 실행 중단. 그 후 함수를 호출한 코드로 되돌아감.
6) 함수 객체의 프로퍼티
함수도 객체이기에 프로퍼티를 가질 수 있다. 일반 객체와는 조금 다르다.
function square(num) {
return num * num;
}
// 객체의 속성을 JSON과 같은 트리구조로 출력. 브라우저에서만 사용 가능
console.dir(square);
① arguments 프로퍼티
- arguments 객체는 함수 호출 시 전달된 인수들의 정보를 담고 있는 순회가능한(iterable) 유사 배열 객체이다.
#유사배열객체(array-like object): 배열의 형태로 인자값 정보를 담고 있지만, 실제로 배열은 아님.
- 함수 내부에서 지역변수처럼 사용된다.
function mul(n1,n2) {
console.log(arguments);
return n1 * n2;
}
mul();
mul(1);
mul(1,2);
mul(1,2,3);
"매개변수는 인수로 초기화된다."
- 매개변수의 갯수보다 인수를 적게 전달했을 때, 인수가 전달되지 않은 매개변수는 undefined로 초기화
- 매개변수의 갯수보다 인수를 더 많이 전달했을 때, 초과된 인수는 무시
런타임 시 호출된 함수의 인자 갯수를 확인하고, 이에 따라 동작을 다르게 정의할 때 argument 객체가 유용하게 사용된다. → 매개변수 갯수가 확정되지 않은 가변 인자 함수를 구현할 때 유용
function sum() {
var result = 0;
for(var i=0;i<arguments.length;i++){
result += arguments[i];
}
return result;
}
console.log('sum(): ' + sum());
console.log('sum(1,2): ' + sum(1,2));
console.log('sum(1,2,3,4): ' + sum(1,2,3,4));
[TIP] 유사배열객체(array-like object)
- 배열의 형태로 인자값 정보를 담고 있지만, 실제 배열이 아닌 객체
- length 프로퍼티를 가진 객체를 의미. (자바스크립트의 모든 배열은 length 프로퍼티를 가진다)
- 배열 메소드를 사용하는 경우 에러가 발생함.
② caller 프로퍼티
- 자신을 호출한 함수를 의미한다.
function testFunc(f){
var result = f();
return result;
}
function whatCaller(){
return "[caller]\n" + whatCaller.caller + '\n';
}
console.log('#1. testFunc 함수가 whatCaller 함수를 호출');
console.log(testFunc(whatCaller));
console.log('#2 main에서 whatCaller 함수 호출');
console.log(whatCaller());
③ length 프로퍼티
- 함수 정의 시 작성된 매개변수 갯수를 의미한다.
function square(num) {
return num * num;
}
console.log('square의 length는> ' + square.length);
④ name 프로퍼티
- 함수명을 의미한다. 기명함수의 경우 함수명을 값으로 갖고, 익명함수의 경우 빈 문자열을 값으로 갖는다.
// 기명 함수 표현식(named function expression)
var namedFunc = function multiply(n1,n2){
return n1 * n2;
}
// 익명 함수 표현식(anonymous function expreesion)
var anonymousFunc = function(n1,n2){
return n1 * n2;
}
console.log(namedFunc.name); //multiply
console.log(anonymousFunc.name); //''
⑤ [[Prototype]] 내부슬롯
- [[Prototype]] 내부슬롯은 프로토타입 객체를 가리킨다.
- 프로토타입 객체에 접근하기 위해 __proto__ 접근자 프로퍼티를 사용한다 (간접적으로 접근 가능)
객체 리터럴{}로 생성한 객체의 프로토타입 객체는 Object.prototype이다. __proto__ 접근자 프로퍼티로 객체에 접근하여 비교하니 true가 출력된다.
모든 객체는 상속을 통해 __proto__ 접근자 프로퍼티를 사용할 수 있다. 그러나 객체가 __proto__ 프로퍼티를 소유하는 것은 아니다.
// : 주어진 객체의 특정 프로퍼티에 대한 디스크립터를 얻을 수 있다.
Object.getOwnPropertyDescriptor(obj, prop);
위 사진을 보면 알 수 있듯, 임의의 객체[}를 인수로 전달하니 undefined가 출력된다.
__proto__ 프로퍼티는 Object.prototype의 접근자 프로퍼티이다.
함수도 객체이므로, __proto__ 접근자 프로퍼티를 통해 프로토타입 객체에 접근할 수 있다.
⑥ prototype 프로퍼티
- 함수가 객체를 생성하는 생성자 함수로 사용될 때, 생성자 함수가 생성한 인스턴스의 프로토타입 객체를 가리킨다.
- 함수 객체만이 소유하는 프로퍼티.
- 일반 객체에는 prototype 프로퍼티가 없다.
7) 함수의 다양한 형태
함수 형태 | 설명 | |
즉시 실행 함수 | 함수의 정의와 동시에 실행되는 함수. (IIFE, Immediately Invoke Function Expression) 최초 한 번만 호출되며 다시 호출할 수 없다. 초기화 처리 등에 사용된다. 즉시 실행 함수는 소괄호로 닫아준다. |
|
내부 함수 | 함수 내부에 정의된 함수(Inner function). 부모함수의 외부에서 접근할 수 없다. | |
재귀 함수 | 자기 자신을 호출하는 함수. 호출을 멈출 수 있는 탈출 조건을 반드시 만들어야 한다. 탈출 조건이 없으면 함수가 무한히 호출되어 stackoverflow 에러가 발생한다. # stackoverflow: Stack 영역의 메모리가 지정된 범위를 넘어갈 때 발생하는 에러. |
|
콜백 함수 | 함수를 명시적으로 호출하는 방식이 아니라, 특정 이벤트가 발생했을 때 시스템에 의해 호출되는 함수. EX) 이벤트 핸들러 처리 |
// 기명 즉시 실행 함수
(function myFunc(){
var a = 3;
var b = 5;
return a*b;
}());
// 익명 즉시 실행 함수
(function(){
var a = 3;
var b = 5;
return a*b;
}());
// 내부함수
function sayHello(name){
var txt = 'Hello ' + name;
var logHello = function(){ console.log(txt); }
logHello();
}
sayHello('홍길동');
logHello('고길동'); // logHello is not defined.
'프로그래밍 > js' 카테고리의 다른 글
lessons 14. 프로토타입 (0) | 2023.02.09 |
---|---|
lessons 13. 타입 체크 (0) | 2023.02.08 |
lessons 11. 객체와 변경불가성(Immutability) (0) | 2023.02.07 |
lessons 10. 객체 (0) | 2023.02.05 |
lessons 9. 타입 변환과 단축 평가 (1) | 2023.02.05 |