조각조각 - 함수

eocode·2024년 3월 6일
0
post-thumbnail

목차

  1. 함수
    1.1. 일반 함수
    1.2. 화살표 함수
    1.3. 콜백 함수
    1.4. 익명 함수
    1.5. 생성자 함수
  2. 함수 선언문 vs 함수 표현식
    2.1. 함수 선언문
    2.2. 함수 표현식
  3. 참고자료

1.함수

자바스크립트는 일반 함수, 화살표 함수, 콜백 함수, 익명 함수, 생성자 함수 등 여러 종류의 함수를 가지고 있습니다. 또 각각은 다양한 특성을 가지고 있습니다. 이번엔 자바스크립트 함수에 대해 알아보도록 하겠습니다.

1.1. 일반 함수

function 키워드를 사용한 '함수 선언문' 으로 선언된 함수

일반 함수는 function 키워드를 사용하여 선언된 함수입니다. 이 선언 방식을 '함수 선언문' 이라고 합니다. 즉 일반 함수는 함수 선언문으로 선언된 함수를 뜻합니다. 일반 함수는 가장 기본적인 함수로 다른 프로그래밍 언어에서도 사용되는 보편적인 선언 구조를 가지고 있습니다.

//함수 선언문, 함수 선언문으로 선언된 함수가 일반 함수입니다.
function fn(){
	console.log("나는 일반함수!");
}

함수 선언문으로 함수 선언시 반드시 함수 이름이 필요합니다. 함수의 이름 없이 함수를 선언하고 싶다면 이름이 없는 함수(화살표 함수 or 익명 함수)를 변수에 할당하는 형태로 함수를 선언해야합니다. 이 변수에 할당해서 함수를 선언하는 방식을 '함수 표현식' 이라 합니다. 화살표 함수, 익명 함수, 함수 표현식은 아래에서 자세히 알아보겠습니다.

변수에 함수 할당이 왜 가능하지?

자바스크립트의 함수는 일급 객체입니다. 따라서 자바스크립트 함수를 일급 함수라고 부르기도 합니다. 이 일급 함수의 특성 덕분에 함수가 변수에 할당될 수 있어 함수 표현식이라는 특이한 형태의 함수 선언이 가능합니다. 또 콜백 함수로 사용이 가능해 고차 함수 등 유연한 활용이 가능합니다.

일급 함수의 특성

  1. 변수나 데이터 구조 안에 할당될 수 있습니다.
  2. 함수의 인자로 전달될 수 있습니다.
  3. 함수의 반환 값으로 사용될 수 있습니다.

일반 함수의 arguments

일반 함수는 함수 선언에 명시하지 않아도 arguments를 암묵적으로 전달받습니다. 따라서 함수의 인자가 arguments에 배열 형태로 들어있습니다.
🚨 arguments는 배열은 아니고 배열과 유사한 객체입니다.

function fn(x, y, z) {
	console.log(arguments[0]);
	console.log(arguments[1]);
	console.log(arguments[2]);
}

fn(1, 2, 3);

--- 출력 결과 ---
1
2
3

코드 처럼 함수 내에서 매개 변수 x를 지칭하지 않고 arguments[0]으로 접근이 가능합니다. arguments[1], arguments[2]도 마찬가지로 접근이 가능합니다.

위에서 보이듯 arguments는 배열의 형태를 띄고 있습니다. 따라서 인자의 개수가 정해지지 않은 경우에 더욱 유용합니다.

function fn() {
	for(let i=0; i<arguments.length;i++){
		console.log(arguments[i]);
	}
}

fn(1, 2, 3,4);

--- 출력 결과 ---
1
2
3
4

함수의 인자의 개수가 정해지지 않은 상태에서도 arguments 배열의 인덱스로 접근이 가능합니다. 개수에 상관 없이 모든 인자에 접근해야할 때 매우 유용합니다.
🚨 arguments는 배열은 아니고 배열과 유사한 객체입니다.

1.2 화살표 함수

( 인자1, 인자2, ...) => { 함수 코드 }; 형태

함수를 더 간결하게 나타내는 표현 방식으로 ES6에서 도입된 문법입니다. 일반함수와 다르게 function 키워드를 사용하지 않고 '=>' 화살표 모양을 이용해 함수를 선언합니다. 인자를 소괄호로 감싼 형태와 화살표 그리고 함수 코드를 감싼 중괄호 구성으로 작성합니다.

화살표 함수 문법

매개변수 부분 축약

기본 형태

매개변수가 없는 상태로 소괄호를 생략할 수 없습니다. 반드시 소괄호가 사용되어야 합니다.

const arrow = () => { console.log("arrow");};

매개변수가 하나인 경우

매개 변수가 하나인 경우 매개변수를 감싸는 소괄호를 생략할 수 있습니다.

const arrow = (a) => { console.log("arrow");};
const arrow = a => { console.log("arrow");};

매개 변수가 2개 이상인 경우

매개 변수가 두개 이상인 경우 매개변수를 감싸는 소괄호를 생략할 수 없습니다. 반드시 소괄호가 사용되어야 합니다.

const arrow = (a, b) => { console.log("arrow");};

함수 코드 부분 축약

값을 바로 반환하는 경우

바로 반환하는 경우 중괄호와 'return'을 생략할 수 있습니다.

const arrow = () => { console.log("arrow");};
const arrow = () => console.log("arrow");

객체를 바로 반환하는 경우

객체를 바로 반환하는 경우 객체를 괄호()로 감싸야 합니다. 괄호 없이 {}만 사용하여 객체를 반환하면 화살표함수가 이를 본문으로 해석합니다.

const arrow = () => ({name : "eo", age : 99});
const arrow = () => {
	return {name : "eo", age : 99}
};

화살표 함수의 arguments

일반 함수는 함수 선언에서 명시하지 않아도 암묵적으로 arguments를 전달받았습니다. 하지만 화살표 함수의 경우 그렇지 않습니다. 화살표 함수는 arguments 객체를 사용할 수 없습니다. 대신 간단한 처리만으로 arguments 객체를 사용하는 것과 비슷하게 동작할 수 있습니다. 바로 인자에서 '전개 연산자' 를 사용하면 됩니다.

const fn = (...arguments) => {
	for(let i=0; i<arguments.length;i++){
		console.log(arguments[i]);
	}
}

fn(1, 2, 3,4);

--- 출력 결과 ---
1
2
3
4

화살표 함수의 인자를 전개 연산자 ...arguments로 설정하면 함수의 모든 인자가 배열로 변환되어 arguments에 저장됩니다. 따라서 인자 개수에 상관없이 모든 인자에 arguments 배열로 접근이 가능합니다.

1.3 콜백 함수

위에서 보았듯이 자바스크립트의 함수는 일급 객체입니다. 그렇기 때문에 일반적으로 적용 가능한 연산을 모두 지원합니다. 따라서 함수의 인자로도 사용이 가능합니다. 이 인자로 사용되는 함수를 콜백함수라고 합니다. 콜백함수는 이름 그대로 나중에 다른 함수(콜백 함수를 인자로 받은 함수)가 호출됩니다.

일급 객체의 특징
일반적으로 적용 가능한 연산을 모두 지원하는 객체

  • 변수에 할당
  • 다른 함수의 인수로 전달
  • 다른 함수의 결과로 반환

콜백 함수 예시

const callbackFn = () => {
	console.log('i am callback function');
};

const fn(f) = () =>{
	for(let i=0;i<3;i++){
		f();
	}
}

위 코드에서 함수 callbackFn이 함수 fn의 인자로 사용됩니다. 즉 callbackFn은 콜백함수가 됩니다. 함수 fn의 구현 사항에 따라 콜백함수 callbackFn이 실행됩니다. 위 코드의 경우 반복문으로 콜백함수 callbackFn이 3번 동작해 'i am callback function'이 3번 출력됩니다.

1.4 익명 함수

function() {}; 형태

익명 함수는 이름이 지정되지 않은 함수를 말합니다. 이름이 지정되지 않은 상태에서 function 키워드와 중괄호를 붙여 작성합니다. 주로 변수에 익명함수를 할당하거나 콜백 함수로 인자를 넘겨주는 경우와 같이 재사용이 필요하지 않을때 사용됩니다.

변수에 할당해서 사용하는경우


const fn1 = function() {
	console.log('익명함수 테스트');
}
const fn2 = function(num) {
	console.log(num+' - 인자 있는 익명함수 테스트');
}

fn1(); //익명함수 테스트
fn2(7); //7 - 인자가 존재하는 익명함수 테스트

익명함수가 변수 fn1, fn2에 할당됩니다. 중괄호를 이용해 함수를 호출할 수 있으며 매개변수가 있는 경우 인자를 중괄호 안에 넣어 함수를 호출할 수 있습니다.

콜백함수로 사용되는 경우(인자로 사용되는 경우)

const fn = (f){
	console.log("ㅁ");
	f();
	console.log("ㅁ");
}

fn(function(){
	console.log("나는 콜백함수에요.")
})

--- 출력 결과---
ㅁ
나는 콜백함수에요.

위 코드 처럼 함수 호출 시 익명 함수를 인자로 넣어 실행할 수 있습니다.

setTimeout

//콜백함수로 익명 함수 사용
setTimeout(function() {console.log("콜백");}, 1000);
		   
//콜백함수로 화살표 함수 사용
setTimeout(()=>{
	console.log("콜백");
}, 1000);

위 방식 외에도 웹 api에서 기본으로 제공하는 setTimout에서 콜백함수를 인자로 넣어 사용가능합니다. 위 두 코드는 동일하게 1초뒤 "콜백"을 출력합니다. 아래 코드는 익명 함수가 아닌 화살표 함수가 콜백함수로 이용된 경우입니다.

1.5 생성자 함수

생성자 함수?

자바스크립트에서 새로운 객체를 만들때 생성자 함수를 사용합니다. 그냥 객체를 선언하면 되지 않느냐? 싶지만 그런 경우가 아니라 일정 틀에 맞춘 객체를 복수개 생성할 때 사용됩니다. 예를 들어 각각 탈것 이름, 바퀴의 개수, 색상 등 동일한 속성값을 가지고 있는 탈것 객체를 다양하게 만들고 싶을 때 사용됩니다. 따라서 이 생성자 함수를 이용하면 동일한 종류의 프로퍼티와 메소드를 가진 다양한 객체를 만들 수 있습니다. 이러한 생성자 함수로 만들어진 객체를 '인스턴스' 라고 부릅니다.

생성자 함수 정의

this 키워드를 이용해 프로퍼티와 메소드 설정이 가능합니다. 생성자 함수가 인자로 받아온 값이 프로퍼티가 되고 필요에 따라 메소드에 활용됩니다.

function MakeBicycle(name, color, wheel){
	this.name = name;
	this.color = color;
	this.wheel = wheel;
	this.go = function(){
		console.log("go!");
	}
} 

객체 인스턴스 생성

생성자 함수와 'new 키워드'를 사용해 객체를 생성할 수 있습니다.

const car = new MakeBicycle('자동차', '검정', 4);
const bicycle = new MakeBicycle('자전거', '노랑', 2);
const train = new MakeBicycle('기차', '파랑', 40);

위 코드의 결과로 car, bicycle, train 객체가 생성됩니다.
🚨 new 키워드가 사용되지 않는다면 일반 함수 동작하고 새로운 객체를 반환하지 않습니다.

//객체 생성 결과

car = {
	name : "자동차",
	color: "검정",
	wheel : 4,
	go : function() {
		console.log("go!");
	}
}
bicycle = {
	name : "자전거",
	color: "노랑",
	wheel : 2,
	go : function() {
		console.log("go!");
	}
}
train = {
	name : "기차",
	color: "파랑",
	wheel : 40,
	go : function() {
		console.log("go!");
	}
}

생성자 함수 vs 객체 리턴 함수

생성자 함수를 공부하니 의문이 생겼습니다. 굳이 생성자 함수를 사용해서 객체를 만들어야 하나? 객체를 리턴하는 함수를 만들고 그 객체에 함수가 받은 인자가 들어가게 하며 되는거 아닌가? 아래에서 생성자 함수와 객체 리턴 함수를 비교하며 의문을 풀어보겠습니다.

생성자 함수는 new 키워드를 사용해 객체(인스턴스)를 생성합니다. 객체 리턴 함수는 단순히 함수를 호출해 객체를 생성합니다. 즉 둘다 객체를 생성하고 반환한다는 공통점을 가지고 있습니다. 하지만 약간의 차이점이 존재합니다. 바로 반환된 객체입니다. 객체 리턴 함수가 리턴하는 것은 일반적인 객체이고 생성자 함수가 리턴하는 것은 '인스턴스' 입니다.

생성자 함수의 반환 객체(인스턴스)

생성자 함수의 반환 객체는 생성자 함수의 프로토타입을 가집니다.

자바스크립트에서 객체는 프로토타입 체인을 통해 프로퍼티와 메소드를 가지기 때문에 생성자 함수로 생성되는 인스턴스는 메모리 측면에서 매우 효율적입니다. 동일한 생성자 함수로 생성되는 인스턴스마다 동일한 프로토타입을 가지고 있기 때문입니다.

객체 리턴 함수의 반환 객체

객체 리턴 함수의 반환 객체는 'Object.prototype'을 가집니다. 이는 모든 객체의 기본 프로토타입니다. 따라서 객체 리턴 함수로 여러개의 객체를 생성하면 모두 'Object.prototype'을 가지는 객체가 됩니다. 프로토타입 체인을 통해 프로퍼티와 메소드를 가지고 올때 그닥 효율적이지 않습니다. 생성되는 객체마다 겹치는 포로토타입이라고는 'Object.prototype'뿐이기 때문입니다.

정리하자면 생성자 함수는 함수 생성자의 프로토타입을 가져 메모리적 측면에서 좋습니다. 하지만 객체 리턴 함수보다 코드가 덜 직관적입니다. 반면에 객체 리턴 함수는 코드는 간단하지만 겹치는 프로토타입이라봐야 'Object.prototype' 뿐이여서 메모리적 측면에서 좋지 않습니다.

생성자 함수객체 리턴 함수
장점- 프로토타입을 활용한 메소드 공유로 메모리 사용을 최적화 할 수 있습니다.
- new 키워드를 사용해 명시적으로 객체를 생성합니다.
- 간단하고 직관적으로 객체 생성
- new 키워드 없이 객체를 생성할 수 있어 사용이 유연합니다.
담점- 코드가 복잡합니다.
- this 바인딩에 대한 이해가 필요합니다.
- 프로토타입을 활용한 공유가 이루어지지 않아 각 객체마다 값을 중복해서 가지게 됩니다.

2. 함수 선언문 vs 함수 표현식

자바스크립트에서 함수를 표현하는 방법은 함수 선언문, 함수 표현식 두가지 입니다. 함수 선언문과 함수 표현식은 작성 방법만 다른 것이 아니라 함수 선언 이전 사용 여부 뿐만 아니라 함수가 메모리에 어떻게 들어가는지도 다릅니다. 아래에서 함수 선언문과 함수 표현식을 비교해가며 정리해보겠습니다.

2.1 함수 선언문

function fn(){
	console.log("함수 선언문");
	return true;
}

'함수 선언문' 은 function 키워드를 사용하여 함수를 정의하는 방식입니다. 가장 기본적인 형태로 다른 프로그래밍 언어에서도 많이 사용되는 방식입니다.

자바스크립트 엔진 평가 단계에서 식별자가 환경 레코드에 등록되고 함수 객체도 환경 레코드에 등록됩니다. 따라서 함수 선언 이전에 함수 호출이 가능합니다. 즉 함수가 선언된 스코프의 최상단에 있는 것과 같이 동작합니다.

💡함수 식별자, 함수 객체 모두 호이스팅 => 함수 선언 코드 이전 함수 실행 가능

2.2 함수 표현식

//익명함수를 변수에 할당하는 경우
const fn1 = function() {
	console.log("함수 표현식1");
}

//화살표 함수를 변수에 할당하는 경우
const fn2 = () => {
 	...
 	console.log("함수 표현식2");
 	...
}

자바스크립트의 함수가 일급 객체이기 때문에 변수에 할당 가능합니다. 이 특성을 활용해 변수에 화살표 함수 또는 익명 함수를 할당해 함수를 선언하는 방식을 '함수 표현식' 이라고 합니다.

두 방식 중 익명 함수를 이용한 함수 표현식은 es6 이전에도 존재해 왔으나 화살표 함수를 이용하여 const, let 변수에 할당하는 함수 표현식은 es6부터 가능합니다. (const, let 타입 변수, 화살표 함수가 es6에 추가되었기 때문입니다.)

함수 표현식의 식별자는 자바스크립트 엔진의 생성 단계일때 환경 레코드에 등록됩니다. 즉 식별자 자체는 호이스팅되어 함수가 선언된 최상단 스코프에 있는 것과 같이 동작합니다. 하지만 값인 함수 객체는 호이스팅 되지 않습니다. 자바스크립트 엔진의 실행 단계가 되어서야 함수 객체가 환경 레코드에 할당됩니다. 따라서 함수 표현식은 함수 객체가 환경 레코드에 할당되기 이전, 즉 함수 선언 코드 이전에 함수 실행이 불가능합니다.

💡 함수 식별자만 호이스팅 => 함수 선언 이전 함수 실행 불가능

변수 타입에 따른 함수 표현식

함수가 할당되는 변수 타입에 따라서도 함수 표현식의 특징이 달라집니다.

  • var 타입 함수 표현식
    1. 자바스크립트 엔진 평가 단계 : 함수 식별자는 환경 레코드에 등록되지만 value 값에 함수 객체가 등록되지는 않습니다. 대신 undefined가 암묵적으로 등록됩니다.
    2. 자바스크립트 엔진 실행 단계 : 함수 선언 코드가 실행 될때 환경 레코드에 value 값으로 함수 객체가 등록됩니다.
  • let, const 타입 함수 표현식
    1. 자바스크립트 엔진 평가 단계 : 함수 식별자는 환경 레코드에 등록되지만 value 값으로 아무 값도 등록되지 않습니다.
    2. 자바스크립트 엔진 실행 단계 : 함수 선언 코드가 실행 될때 환경 레코드에 value 값으로 함수 객체가 등록됩니다.

평가 단계까지 마치면 var 타입 함수 표현식은 undefined 값을 가지고 let, const 타입 함수 표현식은 아무 값도 가지지 않습니다. 따라서 함수 선언 코드 이전 참조하면 var 타입 환경 변수는 undefined가 출력되지만 let, const 타입 함수 표현식은 참조 에러가 발생합니다. 함수 표현식 값이 undefined로 존재해도 함수 실행이 불가능 하기때문에 var, let, const 타입 상관없이 모든 함수 표현식은 함수 선언 이전에 실행이 불가능합니다.

정리하자면 함수 표현식은 var, const, let 타입 관계없이 모두 함수 선언 이전 실행 불가능하며 참조에러가 발생합니다. var 함수 표현식은 선언 이전 참조시 undefined 이며 const, let 함수 표현식은 선언 이전 참조시 참조 에러가 발생합합니다.

⭐️ 이 글에선 자바스크립트 엔진 실행 과정, 호이스팅 개념을 매우 축약하여 적었습니다. 자세한 과정은 이전글 (실행 컨텍스트, 호이스팅)을 참조해주세요. :)

2.3 변수 타입별 간단 정리

varletconst
-ES6부터 등장ES6부터 등장
변수변수상수
식별자 호이스팅 O식별자 호이스팅 O식별자 호이스팅 O
*JSE 평가 단계
선언 O
JSE 평가 단계
선언 O
JSE 평가 단계
선언 O
JSE 평가 단계
초기화 O (undefined)
JSE 평가 단계
초기화 X
JSE 평가 단계
초기화 X
TDZ 존재 XTDZ 존재 OTDZ 존재 O
재선언 O재선언 X재선언 X


*JSE : 자바스크립트 엔진

함수 선언문함수 표현식
일반 함수- 자바스크립트 엔진 평가 단계에서 함수 객체가 환경 레코드에 저장
- 함수 선언 코드 이전 호출 가능
- arguments 객체 존재 O
- 자바스크립트 엔진 실행 단계에서 함수 선언 코드 실행 시 함수 객체 환경 레코드에 저장
- 함수 선언 코드 이전 호출 불가능
- arguments 객체 존재 X
화살표 함수-- 자바스크립트 엔진 실행 단계에서 함수 선언 코드 실행 시 함수 객체 환경 레코드에 저장
- 함수 선언 코드 이전 호출 불가
- arguments 객체 존재 X

3. 참고 자료

profile
프론트엔드 개발자

0개의 댓글