[JavaScript] 함수

빵호·2022년 3월 1일
0

JavaScript

목록 보기
28/28
post-thumbnail

함수

함수는 하나의 단위로 실행되는 문의 집합이다.

모든 함수에는 바디가 있고 바디는 함수를 구성하는 문의 모음이다.

function sayHello() {
  // 함수 바디는 여는 중괄호로 시작
  console.log("Hello");
  console.log("안녕하세요");
  // 닫는 중괄호로 끝
}

예제는 함수 선언(function declaration)의 한 예이고 sayHello라는 함수를 선언했다.

sayHello() // 괄호를 써서 호출할 수 있다.
// Hello
// 안녕하세요

함수를 선언하기만 해서는 바디가 실행되지 않고 함수를 호출(call) 해야 바디가 실행이 된다.

반환 값

함수 호출도 표현식이고, 표현식은 값이 된다. 함수 호출의 값은 반환 값이고 함수 바디 안에 return 키워드를 사용해 함수를 즉시 종료하고 값을 반환할 수 있다.

function getGreeting() {
  return "Hello";
}

getGreeting() // "Hello"

getGreeting 함수를 호출하면 함수 호출은 반환 값이 된다. return을 명시적으로 호출하지 않으면 반환 값은 undefined가 되고 함수는 어떤 타입의 값도 반환할 수 있다.

호출과 참조

자바스크립트에서는 함수도 객체이다. 따라서 객체와 마찬가지로 넘기거나 할당할 수 있다.

함수 호출과 참조의 차이를 이해하는 것은 중요하다. 함수 식별자 뒤에 괄호를 쓰면 호출, 괄호를 쓰지 않으면 함수를 참조하는 것이다.

function getGreeting() {
  return "Hello";
}

getGreeting() // "Hello"
getGreeting // function getGreeting()

함수를 호출하지 않고 참조를 통해 함수를 변수에 할당해 다른 이름으로 함수를 호출 할 수 있다.

const f = getGreeting
f() // "Hello"

함수와 매개변수

함수를 호출할 때 함수 매개변수(argument, parameter)를 이용해 정보를 전달할 수 있다.

function avg(a, b) {
  return (a + b) / 2;
}

이 함수 선언에서 a와 b는 정해진 매개변수(formal argument)라고 하고 함수가 호출되면 정해진 매개변수는 값을 받아 실제 매개변수(actual arguemnt)가 된다.

avg(5 ,10); // 7.5

정해진 매개변수 a, b는 각각 5와 10을 받아 실제 매개변수가 된다. 실제 매개 변수는 변수와 매우 비슷하지만, 함수 바디 안에서만 존재한다.

const a = 5, b = 10;
avg(a, b); 

함수를 호출하면 함수 매개변수는 변수 그 자체가 아니라 그 값을 전달받는다. 그렇기 때문에 첫 행의 변수 a, b는 매개변수 a, b와 다른 변수이다.

function f(x) {
  console.log(`f 내부 : x=${x}`);
  x = 5;
  console.log(`f 내부 : x=${x} (할당 후)`);
}

let x = 3;
console.log(`f를 호출하기 전 : x=${x}`);
f(x);
console.log(`f를 호출한 다음 : x=${x}`);

// 실행 결과
// f를 호출하기 전 : x==3
// f 내부 : x=3
// f 내부 : x=5 (할당 후)
// f를 호출한 다음 : x=5

함수 안에서 x에 값을 할당하더라도 함수 바깥의 변수 x에는 아무 영항이 없다. 이름은 같지만, 둘은 다른 개체이다.

함수 안에서 매개변수에 값을 할당해도 함수 바깥에 있는 변수에는 아무런 영향이 없다. 하지만 함수 안에서 객체 자체를 변경하면, 함수 바깥에서도 바뀐 점이 반영된다.

function f(o) {
  o.message = `f 안에서 수정함 (이전 값 : '${o.message}'`;
}

let o = {
  message : '초기 값'
};

console.log(`f를 호출하기 전 : o.message="${o.message}"`);
f(o);
console.log(`f를 호출한 다음 : o.message="${o.message}"`);

// 실행 결과
// f를 호출하기 전 : o.message="초기 값"
// f를 호출한 다음 : o.message="f 안에서 수정함 (이전 값 : '초기 값')"

함수 f 안에서 객체 o를 수정했고, 이렇게 바꾼 내용은 함수 바깥에서도 o에 그대로 반영되었다. 이것이 원시 값과 객체의 핵심적이 차이이다. 원시 값은 불변, 객체는 바뀔 수 있다.

매변변수 해체

매개변수도 해체가 가능하다.

function getSentence({ subject, verb, object }) {
  return `${subject} ${verb} ${object}`
}

const o = {
  subject : "I", 
  verb : "love", 
  object : "JavaScript"
};

getSentence(o); // "I love JavaScript"

해체 할당과 마찬가지로 프로퍼티 이름은 반드시 유효한 식별자여야 하고, 객체에 해당 프로퍼티가 없는 변수는 undefined를 할당받는다.

function getSentence([ subject, verb, object ]) {
  return `${subject} ${verb} ${object}`
}

const arr = [ "I", "love", "JavaScript" ];

getSentence(arr); // "I love JavaScript"

배열도 해체가 가능하다.

매개변수 기본값

ES6에서는 매개변수에 기본값(default value)을 지정하는 기능도 추가됐다. 일반적으로 매개변수에 값을 제공하지 않으면 undefined가 값으로 할당된다.

function f(a, b = "default", c = 3) {
  return `${a}, ${b}, ${c}`
}

f(5, 6, 7) // 5, 6, 7
f(5, 6) // 5, 6, 3
f(5) // 5, default, 3
f() // undefined, default, 3

객체의 프로퍼티인 함수

객체의 프로퍼티인 함수를 메서드(method)라고 불러서 일반적인 함수와 구별한다.

const o = {
  name : 'Wallace', // 원시 값 프로퍼티
  bark() { return 'Woof!'} // 함수 프로퍼티(메서드)
}

this 키워드

함수 바디 안에는 특별한 읽기 전용 값인 this가 있다.

일반적으로 this는 객체의 프로퍼티인 함수에서 의미가 있고 메서드를 호출하면 this는 호출한 메서드를 소유하는 객체가 된다.

const o = {
  name : 'Wallace', 
  speak() { return `My name is ${this.name}!`} 
}

o.speak(); // My name is Wallace!

o.speak()를 호출하면 this는 o에 묶인다. this는 함수를 어떻게 선언했느냐가 아니라 함수를 어떻게 호출했느냐에 따라 달라진다.

const speak = o.speak;
speak === o.speak; // true; 두 변수는 같은 함수를 가리킴
speak(); // My name is undefined!

함수를 이렇게 호출하면 자바스크립트는 이 함수가 어디에 속하는지 알 수 없으므로 this는 undefined에 묶인다.

함수 표현식과 익명 함수

자바스크립트는 익명 함수(anonymous function)도 지원하고 익명 함수는 함수에 식별자가 주어지지 않는다.

식별자가 없다면 어떻게 호출해야 할까? 답은 함수 표현식(function expression)에 있다.

함수 표현식은 함수 이름을 생략할 수 있다는 점을 제외하면 함수 선언과 문법적으로 완전히 같다.

const f = function() {
  // ...
};

식별자 f가 익명 함수를 가리키기 때문에 일반적인 함수 선언과 마찬가지로 f()로 이 함수를 호출할 수 있다.

익명 함수는 다른 함수나 메서드의 매개변수로 넘길 수도 있고, 객체의 함수 프로퍼티가 될 수도 있다.

화살표 표기법

ES6에서 새로 만든 화살표 표기법(arrow notation)은 function이라는 단어와 중괄호 숫자를 줄이려고 고안된 단축 문법이다.

화살표 함수에는 세 가지 단축 문법이 있다.

  1. function을 생략해도 된다.
  2. 함수에 매개변수가 단 하나뿐이라면 괄호()도 생략할 수 있다.
  3. 함수 바디가 표현식 하나라면 중괄호와 return 문도 생략할 수 있다.

화살표 함수는 항상 익명이고 변수에 할당 가능하지만, function 키워드처럼 이름이 붙은 함수를 만들 수는 없다.

const f1 = function() { return "hello"; } // 화살표 함수 전
const f1 = () => "hello"; // 화살표 함수 후

const f2 = function(name) { retunr `Hello, ${name}`;} // 화살표 함수 전
const f2 = name => `Hello, ${name}`; // 화살표 함수 후

화살표 함수는 일반적인 함수와 다르게 this가 정적으로 묶이고 객체 생성자를 사용할 수 없으며 arguments 변수를 사용할 수 없다.

call과 apply, bind

자바스크립트에서는 call과 apply, bind를 이용해 this를 지정할 수 있다.

call

const bruce = { name : "Bruce" };
const madelinde = { name : "Madelinde" };

function greet() {
  return `Hello, I'm ${this.name}!`;
}

greet(); // Hello, I'm undefined! - this가 어디에도 묶이지 않음
greet.call(bruce); // Hello, I'm Bruce! - this는 bruce

함수를 호출하면서 call을 사용하고 this로 사용할 객체를 넘기면 해당 함수가 주어진 객체의 메서드인 것처럼 사용할 수 있다.

function update(birthYear, occupation) {
  this.birthYear = birthYear;
  this.occupation = occupation;
}
updata.call(bruce, 1949, 'singer'); 
//bruce = { name : "Bruce", birthYear: 1949, occupation: "singer" }

call의 첫 번째 매개변수는 this로 사용할 값이고, 매개변수가 더 있으면 그 매개변수는 호출하는 함수로 전달된다.

apply

updata.apply(bruce, [1949, 'singer']); 
//bruce = { name : "Bruce", birthYear: 1949, occupation: "singer" }

apply는 함수 매개변수를 배열로 받는다는 방법을 제외하면 call과 완전히 같다.

apply는 배열 요소를 함수 매개변수로 사용해야 할 때 유용하다.

bind

const updateBruce = updata.bind(bruce);

updateBruce(1904, "actor");
// bruce = { name : "Bruce", birthYear: 1949, occupation: "singer" }
updateBruce.call(madelinde, 1274, 'king'); 
// bruce = { name : "Bruce", birthYear: 1274, occupation: "king" }
// madelinde은 변하지 않음

bind를 사용하면 함수의 this 값을 영구히 바꿀 수 있다.

profile
늘 한결같이 꾸준히

0개의 댓글