<JS>함수

윤건호·2022년 4월 11일
0

자바스크립트

목록 보기
15/26
post-thumbnail

함수의 생성 시점과 호이스팅

함수 선언문
function add(x , y) {
return x + y;
} // 함수 선언문 전부를 끌어올림

함수 표현식
let sub = function(x , y) {
return x + y;
}; // 변수 선언 부분(let sub) 의 부분만 끌어올림

위 예제는 각각

함수 선언문으로 정의한 함수와 함수 표현식으로 정의한 함수이다.

함수 선언문으로 정의한 함수는 함수 선언문 이전에 호출할 수 있다.
그러나 함수 표현식으로 정의한 함수는 함수 표현식 이전에 호출할 수 없다.

함수 선언문으로 함수를 정의하면 런타임 이전에 함수 객체가 먼저 생성된다.

코드가 한 줄씩 순차적으로 실행되기 시작하는 런타임에는 이미 함수 객체가
생성되어 있고 함수 이름과 동일한 식별자에 할당까지 완료되어 있다.
그러므로 함수 선언문 이전에 함수를 참조할 수 있으며 호출할 수도 있다.

이처럼 함수 선언문이 코드의 선두로 끌어 올려진 것처럼 동작하는
자바스크립트 고유의 특징을 함수 호이스팅이라 한다.

변수 할당문의 값은 할당문이 실행되는 시점, 즉 런타임에 평가되므로
함수 표현식의 함수 리터럴도 할당문이 실행되는 시점에 평가되어 함수 객체가 된다.

따라서 함수 표현식으로 함수를 정의하면 함수 호이스팅이 발생하는 것이 아니라
변수 호이스팅이 발생한다.

함수 호이스팅과 변수 호이스팅

우선 공통점을 이야기하자면 런타임 이전에 자바스크립트 엔진에 의해
먼저 실행되어 식별자를 생성한다는 점이 동일하다.

차이점은 var키워드로 선언된 변수는 undefined 로 초기화되고,
함수 선언문을 통해 암묵적으로 생성된 식별자는 함수 객체로 초기화된다.

따라서 var키워드를 사용한 변수 선언문 이전에 변수를 참조하면
변수 호이스팅에 의해 undefined로 평가되지만
함수 선언문으로 정의한 함수를 함수 선언문 이전에 호출하면
함수 호이스팅에 의해 호출이 가능하다.

화살표 함수

ES6에서 도입된 화살표 함수(arrow function)는 function 키워드 대신

화살표를 사용해 좀 더 간략한 방법으로 함수를 선언할 수 있다.

const add = (x,y) => x + y; // 익명 함수
console.log(add(2,5)); // 7

화살표 함수는 생성자 함수로 사용할 수 없다
기존 함수와 this 바인딩 방식이 다르고, prototype 프로퍼티가 없으며,
argument 객체를 생성하지 않는다.

함수 호출

함수는 함수를 가리키는 식별자와 한 쌍의 소괄호()인 함수 호출 연산자로 호출한다.

함수를 호출하면 현재의 살행 흐름을 중단하고 호출된 함수로 실행흐름을 옮긴다.
이때 매개변수에 인수가 순서대로 할당되고 함수 몸체의 문들이 실행되기 시작한다.

function add(x,y) {
return x + y;
}

let result = add(1,2);
인수 1과 2가 매개변수 x와 y에 순서대로 할당되고 함수 몸체의 문들이 실행된다. 필요한 만큼의 인자를 넣어준다.

인자의 유효범위

function add(x,y) {
console.log(x,y); // 2 5
return x + x
add(2,5);
add 함수의 매개변수 x,y는 함수 몸체 내부에서만 참조할 수 있다.
console.log(x,y); // ReferenceError : x is not defined

function add(x,y) {
return x + y;
}
console.log(add(2)); // NaN

위 예제 매개변수 x에는 인수 2가 전달되지만, 매개변수 y에는 전달할 인수가 없다.
따라서 매개변수 y는 undefined 로 초기화된 상태 그대로다.

따라서 함수 몸체의 문 x + y는 2 + undefined와 같으므로 NaN이 반환된다.

참고로 매개변수보다 인수가 더 많은 경우 초과된 인수는 무시된다.

인수 확인
funcion add(x,y) {
return x + y;
}
console.log(add(2)); // NaN
console.log(add('a', 'b')); // 'ab'

위 코드에서 개발자의 의도는 아마 2개의 숫자타입을 받아 합계를 반환하려는 것으로 추측된다.

하지만 코드상으로 어떤 타입의 인수를 전달해야 하는지, 어떤 타입의 값을 반환하는지
명확하지 않다.

위 코드는 자바스크립트 문법상 어떠한 문제도 없으므로 자바스크립트 엔진은

아무런 이의 제기없이 위 코드를 실행할 것이다.

그 이유는 다음과 같다.

1. 자바스크립트 함수는 매개변수와 인수의 개수가 일치하는지 확인하지 않는다.
2.자바스크립트는 동적 타입 언어다. 따라서 자바스크립트 함수는 매개변수의 타입을 사전에 지정할 수 없다.

따라서 자바스크립트의 경우 함수를 정의할 때

적절한 인수가 전달되었는지 확인할 필요가 있다.

function add(x,y) {
if(typeof x !== 'number' || typeof y !== 'number') {
// 매개변수를 통해 전달된 인수의 타입이 부적절한 경우 에러를 발생시킨다.
throw new TypeError('인수는 모두 숫자 값이어야 합니다.')
}
return x + y;
}
console.log(add(2)); // TypeError: 인수는 모두 숫자 값이어야합니다.
console.log(add('a','b')); // TypeError: 인수는 모두 숫자 값이어야합니다.

매개변수의 최대 개수

ECMAscript 사양에서는 매개변수의 최대 개수에 대해 명시적으로 제한하고 있지 않다.

그렇다면 최대 몇 개 까지 사용하는 것이 좋을까?

매개 변수가 많아지면 함수를 호출할 때 전달해야 할 인수의 순서를 고려해야 한다.

이는 함수를 이해하기 어렵게 만들고 실수 발생의 가능성을 높인다.

이상적인 함수는 한 가지 일만 해야 하며 가급적 작게 만들어야 한다.
따라서 매개변수는 최대 3개이상을 넘지 않는 것을 권장한다.

반환문

함수는 return 키워드와 표현식(반환값)으로 이뤄진 반환문을 사용해 실행 결과를

외부로 반환(return) 할 수 있다.

function multiply(x,y) {
return x * y; // 반환문
console.log('실행되지 않는다.');
}
// 함수 호출은 반환값으로 평가된다.
let result = multiply(3,5);
console.log(result); // 15

반환문은 두 가지 역할을 한다.
첫째, 반환문은 함수의 실행을 중단하고 함수 몸체를 빠져나간다.
따라서 반환문 이후에 다른 문이 존재하면 그 문은 실행되지 않고 무시된다.

둘째, 반환문은 return 키워드 뒤에 오는 표현식을 평가해 반환한다.
return 키워드 뒤에 반환값으로 사용할 표현식을 명시적으로 지정하지 않으면
undefined 가 반환된다.

참조에 의한 전달과 외부 상태의 변경

function changeVal(primitive, obj) {
primitive += 100;
obj.name = 'Kim';
}
매개변수 primitive는 원시 값을 전달받고, 매개변수 obj는 객체를 전달받는다.

외부 상태
let num = 100;
let person = {name : 'Lee'};
console.log(num); // 100
console.log(person); // {name : "Lee"}

원시 값은 값 자체가 복사되어 전달되고 객체는 참조 값이 복사되어 전달된다.
changeVal(num, person);
원시값은 원본이 훼손되지 않는다.
console.log(num); // 100
객체는 원본이 훼손된다.
console.log(person); // {name : "Kim"}

profile
더 배우고 싶은 프론트엔드 개발자 윤건호입니다.

0개의 댓글