[JS Data-types(데이터 타입)] 함수(function)

Chanki Hong·2022년 11월 13일
0

JavaScript

목록 보기
13/30
post-thumbnail

함수(function)

  • 특정 문(statement)을 하나의 단위로 실행하는 집합.
  • 파라미터(매개변수; input)를 처리하여 결과(output)를 만듬.
  • 함수는 자신이 존재하는 컨텍스트(환경)에 따라 다양한 모습을 보임.
  • 일면은 코드를 재사용하는 수단.
  • 모든 함수는 바디{} 가 있고, 바디는 함수를 구성하는 문의 모음.
  • JS에서는 함수도 객체.

함수 정의 방법

함수 선언문(Function Declaration)

  • 가장 일반적인 함수 정의 방법.
// hello라는 이름의 함수 선언.
function hello(name) {
  return 'Hello, ' + name + '!';
}
// 함수 선언만으로는 함수 바디가 실행되지 않음.
  • 호이스팅에 의해 함수가 스코프 맨 위로 끌어올려지무로 어디서든 호출 가능.
  • 코드의 가독성이 높아짐.
  • 함수를 선언한 스코프 내에서만 호출 가능.

함수 표현식(Function Expression; 익명함수)

  • 식별자가 없는 선언.
  • 함수 표현식은 함수가 익명이 될 수도 있음을 의미.
  • 함수 표현식으로 익명함수를 만들고 그 함수를 바로 변수 등에 할당 하기 위해.
const f = function () {
  // ...
};
  • 함수나 메서드의 매개변수로 넘길 수도 있고, 객체의 메서드 등 다양한 스코프에서 활용 가능.
  • 호이스팅에 영향을 받지 않음.(식별자만 호이스팅.)
greet(); // ReferenceError: Cannot access 'greet' before initialization
const greet = function(name) {
  return `Hello, ${name}!`;
};
  • 함수 안에서 자신을 호출할 때(재귀) 이용.
const g = function f(stop) {
  if (stop) console.log(`f stopped`);
  f(true);
};
g(false); // 외부에서는 f에 접근 불가.

화살표 함수(화살표 표기법)

  • function 키워드 생략.
  • 간결하게 표현 가능.
  • 항상 익명 함수. (변수 할당은 가능, 이름 붙은 함수는 불가)
// name(파라미터, 매개변수)을 받아 문장을 반환하는 화살표 함수.
const hello = (name) => {
  return `Hello, ${name}!`;
};
const result = hello('world');
console.log(result);

// 화살표 함수의 매개변수 또는 바디의 표현식이 하나라면 괄호와 return 생략 가능.
const hello = name => `Hello, ${name}!`; // 간결함 상승.
const result = hello('world');
console.log(result);
  • this 가 정적으로 묶임.(전역 변수로만 작동함)
const obj1 = {
  name: "Alice",
  sayHello: () => {
    console.log(`Hello, ${this.name}!`);
  },
};

const obj2 = {
  name: "Bob",
  sayHello: obj1.sayHello,
};

obj1.sayHello(); // "Hello, undefined!" 출력
obj2.sayHello(); // "Hello, undefined!" 출력
  • 객체 생성자로 사용 불가.
  • 확산 연산자 사용 불가.

함수 호출(call, run, execute, invoke)

  • 함수의 식별자(이름) 다음에 () (파라미터; 매개변수) 작성하면 함수의 바디 실행.
  • JS는 어떠한 표현식(주로 식별자표현식) 뒤에 괄호가 있으면, 함수라고 간주하고 호출.
  • 함수가 아닌 값 뒤에 괄호를 붙이면 에러.
  • console.log() 또한 함수이며 매개변수 자리에 값(표현식 등)을 넣었음.
function hello(name) {
  return `Hello, ${name}!`;
}
console.log(hello('world')); // hello 함수 호출하고 콘솔에 출력.
  • 함수 호출은 표현식 이고 즉, 값이 됨.
  • 함수 호출의 값은 반환 값.
  • 반환값: return (예외문) 키워드를 이용하여 함수를 즉시 종료하고 값을 반환. (반환 값)

함수 참조

  • 함수의 식별자 다음에 () 를 작성하지 않으면 참조하는 것이며, 그 함수는 실행되지 않음.
function getGreeting() {
  return 'Hello world!';
}
console.log(getGreeting); // [Function: getGreeting] 출력.
  • 함수를 참조하기만 할 수 있음은 JS를 매우 유연한 연어로 만듬.
  • 함수를 다른 변수(또는 객체 프로퍼티, 배열 요소)에 할당하고 다른 이름으로 함수 호출 가능.
// 변수 할당.
function getGreeting() {
  return 'Hello world!';
}
const f = getGreeting;
f();
console.log(f());

// 객체 프로퍼티로 할당.
function getGreeting() {
  return 'Hello world!';
}
const o = {};
o.f = getGreeting;
console.log(o.f());

// 배열 요소로 할당.
function getGreeting() {
  return 'Hello world!';
}
const arr = [1, 2, 3];
arr[1] = getGreeting; // 배열에 함수 할당.
console.log(arr); // [ 1, [Function: getGreeting], 3 ] 출력.
console.log(arr[1]());

매개변수(parameter, argument)

  • 매개변수는 함수에 정보를 전달하기 위함.
  • 함수가 호출 되기 전에는 존재하지 않음.
  • 또한 매개변수는 함수 바디 안에서만 존재.
  • 함수 안에서 변수에 값을 할당해도 블록문 밖의 변수에는 아무 영향이 없음.
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=3
즉, 두개의 x는 다른 개체.
*/
  • 함수 안에서 객체를 변경(수정)하면, 바뀐 점이 반영.
  • 원시 값과 객체의 핵심적인 차이점.
  • 원시 값은 불변이기에 수정이 불가. (원시 값 자체가 불변, 다른 값 할당을 의미하지 않음.)
  • 반면 객체는 변경(수정) 할 수 있음.
function f(o) {
  o.message = '함수에서 객체를 수정'; // let o와 같은 객체를 가리킴. 그것을 수정.
  o = { // 내부의 새로 할당한 객체는 전혀 새로운 객체임. 새로운 객체를 가리킴.
    message: '함수에서 만든 객체!',
  };
  console.log(`함수 내부의 o.message값: ${o.message}`);
}

let o = {
  message: '초기값',
};
console.log(`f를 호출하기 전 o.message값: ${o.message}`);
f(o);
console.log(`f를 호출한 뒤 o.message값: ${o.message}`);
/*
f를 호출하기 전 o.message값: 초기값
함수 내부의 o.message값: 함수에서 만든 객체!
f를 호출한 뒤 o.message값: 함수에서 객체를 수정
*/

매개변수의 개수

  • 어떤 함수를 호출하든 정해진 매개변수 숫자와 관계없이 매개변수를 전달 가능.
  • 정해진 매개변수의 값보다 적게 제공하면 암시적으로 undefined가 할당됨.
function f(x) {
  return `f에서의 x값은: ${x}`;
}
console.log(f()); // f에서의 x값은: undefined 출력.

매개변수 해체

  • 객체를 매개변수로 해체 가능.
  • 프로퍼티가 없는 변수는 undefined 할당.
function getSentence({ subject, verb, object }) {
  return `${subject} ${verb} ${object}`;
}

const o = {
  subject: 'I',
  verb: 'love',
  object: 'javascript',
};
console.log(getSentence(o)); // I love javascript 출력.
  • 배열 또한 매개변수로 해체 가능.

매개변수 기본값

function f(a, b) {
  const apple = a || 1;
  const banana = b || 3;
  return `a + b = ${apple + banana}`;
}
console.log(f()); // a + b = 4 출력.
console.log(f(8)); // a + b = 11 출력.
console.log(f(,8)); // SyntaxError: Unexpected token ','.
  • ES6.
function f(a = 1, b = 3) {
  return `a + b = ${a + b}`;
}
console.log(f()); // a + b = 4 출력.
console.log(f(8)); // a + b = 11 출력.
console.log(f(,8)); // SyntaxError: Unexpected token ','.
  • 화살표 함수도 이용 가능.
const f = (a = 1, b = 3) => `a + b = ${a + b}`;
console.log(f()); // a + b = 4 출력.
console.log(f(8)); // a + b = 11 출력.
console.log(f(,8)); // SyntaxError: Unexpected token ','.

메서드(method); 함수 프로퍼티

  • 객체의 프로퍼티인 함수를 메서드(method)라 하고, 일반적인 함수와 구별.
  • 객체 리터럴에 메서드 추가 가능.
// 익명함수로 메서드를 선언함.
// function 뒤에 함수 식별자를 명시해도 되지만, 불러올때 어처피 key값으로 불러옴.
const dog = {
  name: '멍멍이',
  sound: '멍멍!',
  say: function () {
    console.log(this.sound);
  },
};

dog.say(); // 멍멍! 출력.

// 조금 더 간결한 방법(ES6)
const dog = {
  name: '멍멍이',
  sound: '멍멍!',
  say() {
    console.log(this.sound);
  },
};

dog.say(); // 멍멍! 출력.

this 키워드

  • 화살표 함수에서는 this 키워드 사용 불가. (undefined 출력; 상속되지 않음)
  • 함수 바디에서 특별한 읽기 전용 값 this.
  • 메서드를 호출하면 this는 해당 메서드를 소유하는 객체가 됨.
  • this 는 함수의 선언이 아닌 호출 방법에 따라 변함.
const dog = {
  name: '멍멍이',
  sound: '멍멍!',
  say() {
    console.log(this.sound);
  },
};

const cat = {
  name: '야옹이',
  sound: '야옹~',
};

cat.say = dog.say;
dog.say(); // 멍멍! 출력.
cat.say(); // 야옹~ 출력.

const catSay = cat.say;
catSay(); // this가 가리키는 상위 객체 등이 없음. undefined 출력.

console.log(dog.say === cat.say && catSay === dog.say && catSay === cat.say);
// true 출력. 같은 객체를 가리키는 메모리 주소임.

call 메서드

  • 함수.call(객체 식별자)
  • 모든 함수에서 사용 가능.
  • 함수를 호출하면서 call 을 사용하여 객체를 넘기면, 객체의 메서드 처럼 this 사용 가능.
const bruce = { name: 'Bruce' };
const madeline = { name: 'Madeline' };

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

console.log(greet()); // Hello, I'm undefined~
console.log(greet.call(bruce)); // Hello, I'm Bruce~
console.log(greet.call(madeline)); // Hello, I'm Madeline~
  • call 의 매개변수가 여러개라면...?
  • 함께 호출한 함수와 나눠야 하는데, 첫번째 매개변수만 call 로 사용. (즉 this)
  • 첫번째 매개변수를 제외하고는 함께 호출된 함수로 전달됨.
  • 함수.call(객체 식별자, 함수 매개변수)
const bruce = { name: 'Bruce' };
const madeline = { name: 'Madeline' };

function update(birthYear, occupation) {
  this.birthYear = birthYear;
  this.occupation = occupation;
}
// call의 매개변수중 첫번째만 call에 해당 즉 this로 사용됨.
// 나머지 두개는 호출한 함수에 전달.
update.call(bruce, 1949, 'singer');
update.call(madeline, 1942, 'actress');
console.log(bruce); // { name: 'Bruce', birthYear: 1949, occupation: 'singer' }
console.log(madeline); // { name: 'Madeline', birthYear: 1942, occupation: 'actress' }

apply 매서드

  • 함수.apply(객체 식별자, [함수 매개변수])
  • 매개변수를 배열로 받는 것을 제외하면 call 동일.
const bruce = { name: 'Bruce' };
const madeline = { name: 'Madeline' };
function update(birthYear, occupation) {
  this.birthYear = birthYear;
  this.occupation = occupation;
}
// apply의 첫째 변수는 apply에 해당하고 (즉, this)
// 배열로 된 부분은 함수에 전달됨.
update.apply(bruce, [1949, 'singer']);
update.apply(madeline, [1942, 'actress']);
console.log(bruce); // { name: 'Bruce', birthYear: 1949, occupation: 'singer' }
console.log(madeline); // { name: 'Madeline', birthYear: 1942, occupation: 'actress' }

// 내장 함수 `Math.min` 과 `Math.max` 에서 대표적인 예제.
const arr = [2, 3, -5, 15, 7];
console.log(Math.min.apply(null, arr)); // -5
console.log(Math.max.apply(null, arr)); // 15
/*
this의 값에 null을 쓴 이유는 Math.min과 Math.max가 this와 관계없이 동작하기 때문.
*/
  • 확산 연산자(...)를 사용하여 apply와 같은 결과 얻을 수 있음. (아직 이해못함)

bind 매서드

  • this 값을 항상 고정.
const bruce = { name: 'Bruce' };
const madeline = { name: 'Madeline' };
function update(birthYear, occupation) {
  this.birthYear = birthYear;
  this.occupation = occupation;
}
const updateBruce = update.bind(bruce);
updateBruce(1904, 'actor');
console.log(bruce); // { name: 'Bruce', birthYear: 1904, occupation: 'actor' }
updateBruce.call(madeline, 1274, 'king');
console.log(bruce); // { name: 'Bruce', birthYear: 1274, occupation: 'king' }
  • 다만 함수의 동작을 영구적으로 바꾸므로 찾기 어려운 버그 원인이 될 수 있음.
  • 함수의 this가 묶이는 곳을 정확히 한 뒤, 사용해야 유용.

0개의 댓글