자바스크립트 딥 다이브 12장 함수 part-1

houndhollis·2024년 3월 10일
0

헉. 일요일. 말도 안돼. !
수요일 날 에비군을 간다. 함수 파트는 길어서 두번에 나눠서 하기로 했다!

그러면,, 함수 다~ 쓰는 화요일이면 수요일은 조금 늦게까지 자도 될꺼같다..흐흐

함수

함수는 자바스크립트에서 가장 중요한 핵심 개념이다. 또 다른 자바스크립트의 핵심 개념인 스코프, 실행 컨텍스트, 클로저, 생성자 함수에 의한 객체 생성, 메서드, this, 프로토 타입, 모듈화 등이 모두 함수와 깊은 관련이 있다.

프로그래밍 언어의 함수도 수학의 함수와 같은 개념이다. 함수 f(x + y) = x + y 를 자바스크립트이 함수로 표현해 보자

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

add(2,5) // 7;

프로그래밍 언어의 함수는 일련의 과정을 문으로 구현하고 코드 블록으로 감싸서 하나의 실행 단위로 정의한 것이다.

함수 내부로 입력을 전달 받는 변수를 - 매개 변수 (parameter)
입력을 - 인수 (argument)
출력을 반환값 이라고 한다.

또한 함수는 값이며, 여러 개 존재할 수 있으므로 특정 함수를 구별하기 위해 식별자인 함수 이름을 사용할 수 있다.

함수는 함수 정의를 통해 생성한다. 자바스크립트의 함수는 다양한 방법으로 정의할 수 있다.

함수 선언문을 통한 정의

// 함수 정의
function add(x,y) {
	return x + y;
}

함수는 정의만으로 함수가 실행되는 것은 아니다. 수학의 함수처럼 미리 정의된 일련의 과정을 실행하기 위해 필요한 입력, 즉 인수를 매개변수를 통해 함수에 전달하여 함수의 실행을 명시적으로 지시해야 한다.

  • 이를 함수 호출 이라고 한다.
// 함수 호출
let result = add(2,10);

// 인수 2, 10 을 add 함수에 전달, 반환값 12을 반환한다.
console.log(result) // 12;

함수를 사용하는 이유

함수는 필요할 때 여러 번 호출할 수 있다. 즉, 실행 시점을 개발자가 결정할 수 있고 몇 번이든 재사용이 가능하다. 동일한 작업을 반복적으로 수행해야 한다면 같은 코드를 여러번이 아니라 정의된 함수를 재사용 하는 것이 효율적이다.

함수를 사용하지 않고 여러번 반복 한다면, 코드를 수정해야 할 때 중복된 횟수만큼 코드를 수정해야 한다. 따라서 중복된 횟수에 비례해서 코드 수정에 걸리는 시간이 증가한다.

또한 유지 보수의 편의성을 높이고 실수를 줄여 코드의 신뢰성을 높이는 효과가 있다.
"코드는 동작하는 것만이 존재 목적은 아니다, 코드는 개발자를 위한 문서이기도 하다 따라서 사람이 이해할 수 있는 코드, 즉 가독성이 좋은 코드가 좋은 코드다"


함수 리터럴

자바스크립트의 함수는 객체 타입의 값이다. 따라서 숫자 값을 숫자 리터럴로 생성하고 객체를 객체리터럴로 생성하는 것처럼 함수도 함수 리터럴로 생성할 수 있다.

// 변수에 함수 리터럴 할당
let foo = function add(x ,y) {
	return x + y;
}

리터럴은 사람이 이해할 수 있는 문자 또는 약속된 기호를 사용해 값을 생성하는 표기 방식을 말한다. 따라서 함수 리터럴도 평가되어 값을 생성하며, 이 값은 객체다. 즉, 함수는 객체다.

함수는 객체지만 일반 객체와는 다르다. 일반 객체는 호출할 수 없지만 함수는 호출할 수 있다. 그리고 일반 객체에는 없는 함수 객체만의 고유한 프로퍼티를 갖는다.


함수 정의

함수 정의란 함수를 호출하기 이전에 인수를 전달받을 매개변수와 실행할 문들, 글고 반환할 값을 지정하는 것을 말한다.

// 함수 선언문
function add(x, y){
	return x + y;
}

// 함수 표현식
let add = function(x, y){
	return x + y;
}

// Function 생성자 함수
let add = new Function('x','y', 'return x + y');

// 화살표 함수
let add = (x, y) => x + y;

함수 선언문은 함수 리터럴과 형태가 동일하다 단, 함수 리터럴은 함수의 이름을 생략할수 있으나 함수 선언문은 함수 이름을 생략할 수 없다.

함수 선언문은 표현식이 아닌 문이다. 개발자 도구의 콘솔에서 함수 선언문을 실행하면 완료값이 undefined 가 출력된다. 함수 선언문이 만약 표현식인 문이라면 완료 값 undefined 대신 표현식이 평가되어 생성된 함수가 출력되어야 한다.

표현식이 아닌 문은 변수에 할당할 수 없다. 함수 선언문도 표현식이 아닌 문이므로 변수에 할당할 수 없다.

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

console.log(add(2, 10)); // 12

// 함수 선언문이 변수에 할당되는 것처럼 보인다. 

이렇게 동작하는 이유는 자바스크립트 엔진이 코드의 문맥에 따라 동일한 함수 리터럴을 표현식이 아닌 문인 함수 선언문으로 해석하는 경우와 표현식인 문인 함수 리터럴 표현식으로 해석하는 경우가 있기 때문이다.

자바스크립트 엔진은 함수 이름이 있는 함수 리터럴을 단독으로 사용하면 함수 선언문으로 해석하고, 함수 리터럴이 값으로 평가되어야 하는 문맥, 예를들어 변수에 할당하거나 피 연산자로 사용하면 함수 리터럴 표현식으로 해석하기 때문이다,

// 기명 함수 리터럴을 단독으로 사용하면 함수 선언문으로 해석된다.
// 함수 선언문에서는 함수 이름을 생략할 수 없다.
function foo() { console.log('foo'); }
foo(); // foo

// 함수 리터럴을 피연산자로 사용하면 함수 선언문이 아니라 함수 리터럴 표현식으로 해석된다.
// 함수 리터럴에서는 함수 이름을 생략할 수 있다.
(function bar() { console.log('bar'); });
bar() // ReferenceError

위 예제에서 단독으로 사용된 함수 리터럴 (foo)은 함수 선언문으로 해석된다. 하지만 그룹 연산자 () 내에 있는 함수 리터럴 (bar) 은 함수 선언문으로 해석되지 않고 함수 리터럴 표현식으로 해석된다.

자바스크립트 엔진은 함수 선언문을 해석해 함수 객체를 생성한다. 이때 함수 이름은 함수 몸체 내부에서만 유효한 식별자이므로 함수 이름과는 별도로 생성된 함수 객체를 가리키는 식별자가 필요하다.

함수는 함수 이름으로 호출하는 것이 아니라 함수 객체를 가리키는 식별자로 호출한다.


함수 표현식

자바스크립트의 함수는 값처럼 변수에 할당 할 수도 있고 프로퍼티 값이 될 수도 있으며 배열의 요소가 될 수도 있다. 이처럼 값의 성질을 갖는 객체를 일급 객체 라 한다.
자바스크립트의 함수는 일급 객체다. 함수가 일급 객체라는 것은 함수를 값처럼 자유롭게 사용할 수 있다는 의미다.

함수 선언문에서 살펴본 바와 같이 함수를 호출할 때는 함수 이름이 아니라 함수 객체를 가리키는 식별자를 사용해야 한다.

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

// 함수 객체를 가리키는 식별자로 호출
console.log(foo(2, 10)); // 12

// 함수 이름으로 호출하면 ReferenceError가 발생한다.
console.log(add(2, 10)); // ReferenceError

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

// 함수 호출
console.log(add(2,10)); // 12
console.log(sub(2,10)); // TypeError: sub is not a function

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

const sub = function (x, y) {
	return x + y;  
}

함수 선언문으로 정의한 함수와 함수 표현시긍로 정의한 함수의 생성 시점은 다르다.
모든 선언문이 그렇듯 함수 선언문도 코드가 한 줄씩 순차적으로 실행되는 시점인 런타임 이전에 자바스크립트 엔진에 의해 먼저 실행된다.

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

그러면 sub는 왜 타입에러가 났을까?
바로 변수 선언은 런타임 이전에 실행되어 undefined로 초기화되지만 변수 할당문의
값은 할당문이 실행되는 시점, 즉 런타임에 평가되므로 함수 표현식의 함수 리터럴도 할당문이 실행되는 시점에 평가되어 함수 객체가 된다.

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


화살표 함수

ES6 에서 도입된 화살표 함수는 function 키워드 대신 화살표 => 를 사용해 좀 더 간략한 방법으로 함수를 선언할 수 있다.

화살표 함수는 항상 익명 함수로 정의 한다.

const add = (x,y) => x + y;

console.log(add(2, 10)) // 12;

화살표 함수는 기존의 함수 선언문 또는 함수 표현식을 완전히 대체하기 위해 디자인된 것은 아니다. 화살표 함수는 기존의 함수보다 표현만 간략한 것이 아니라 내부 동작 또한 간략화 되어 있다.

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


함수 호출

함수는 함수를 가리키는 식별자와 한 쌍의 소괄호인 함수 호출 연산자 내에는 0개 이상의 인수를 쉼표로 구분해 나열한다. 함수를 호출하면 현재의 실행 흐름을 중단하고 호출된 함수로 실행 흐름을 옯긴다.

함수를 실행하기 위해 필요한 값을 함수 외부에서 내부로 전달할 필요가 있는 경우,
매개변수를 통해 인수를 전달한다. 인수는 값으로 평가될 수 있는 표현식이어야 한다. 인수는 함수를 호출할 때 지정하며, 개수와 타입에 제한이 없다.

매개 변수는 함수 몸체 내부에서만 참조할 수 있고 함수 몸체 외부에서는 참조할 수없다.

즉, 매개변수의 스코프는 함수 내부다

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

add(2, 10);

console.log(x,y); // ReferenceError

함수는 매개변수의 개수와 인수의 개수를 체크하지 않는다, 인수가 부족해서 인수가 할당되지 않은 매개변수의 값은 undefined 이며, 오히려 더 많을 경우 초과된 인수는 무시된다.

사실 인수가 그냥 버려지는 것은 아니고ㅓ arguments 객체의 프로퍼티로 보관된다.

function add(x, y) {
  console.log(arguments);
  // Arguments(3) [2, 5, 12, callee: f, Symbol(Symbol.iterator): f]
	return x + y; 
}

add(2, 5, 12);

오늘은 함수 12장을 두파트로 나뉘어서 여기가 Part1 이다 !! 그럼 내일 출근을 위해 준비..!

profile
한 줄 소개

0개의 댓글