본문 바로가기

JavaScript

함수

순수함수와 부수효과

순수함수는 인수가 전달되면 늘 같은 출력값을 반환하며 아무런 부수 효과가 없습니다. 

 

*부수효과란 함수의 외부를 바꿔놓는 것을 일컫는 용어입니다.

 

function add(num1, num2) {
  return num1 + num2;
}


console.log(add(1, 5)); // 6 출력

함수 add는 매개변수 num1, num2에 인수를 전달받아 + 연산한 값을 반환하는 함수입니다. 해당 함수는 인수로 전달하는 값이 동일하면 여러 번 호출해도 매번 같은 값을 반환하는데 이것을 순수 함수라고 합니다.

 

function addRandom(num1) {
  return num1 + Math.random();
}

console.log(addRandom(5)); // 매 실행마다 다른 값 반환

함수 addRandom은 매개변수 num1에 인수를 전달받아 해당 인수에 랜덤 한 값을 더하여 반환하는 함수입니다. 해당 함수는 인수로 전달하는 값이 동일하지만 매 실행마다 다른 값을 반환하는데 이것을 비순수 함수라고 합니다.

 

let previousResult = 0;

function addMoreNumbers(num1, num2) {
  const sum = num1 + num2;
  previousResult = sum;
}

함수 addMoreNumbers는 인수로 전달받은 값을 + 연산 후 외부에 선언된 변수 previousResult에 재할당하는 함수입니다. 해당 함수는 외부의 변수를 바꿔놓는 부수효과를 만들어내기 때문에 비순수 함수입니다.

 

const hobbies = ["sports", "cooking"];

function printHobbies(hob) {
  hob.push("New Hobby");
  console.log(hob);
}

printHobbies(hobbies); // ['sports', 'cooking', 'New Hobby'] 출력
console.log(hobbies); // ['sports', 'cooking', 'New Hobby'] 출력

함수 printHobbies는 인수로 전달받은 배열에 새로운 요소를 추가하여 콘솔에 출력하는 함수입니다. 해당 함수는 인수로 전달받은 배열에 요소를 추가했는데 배열은 참조값에 해당하기 때문에 요소를 추가할 경우 외부에 선언된 hobbeis 배열도 변경이 되기 때문에 부수효과를 만들어낸 비순수 함수입니다.

 

 

 

순수 함수와 비순수 함수

JavaScript에서 부수 효과가 없는 순수 함수를 지향하는 것은 좋은 생각입니다. 단순히 해당 함수를 예측 가능하며, 만약 함수의 정의를 모른 채 호출한 경우 외부의 값을 변경하지 않는 것이 좋기 때문입니다. 하지만 애플리케이션 구축 시 부수 효과를 피하는 것은 불가능합니다. 함수는 이벤트를 설정해야 하거나 서버에 데이터를 보내야 할 필요가 있을 수도 있기 때문입니다. 

 

위의 이유로 부수 효과를 완전히 제거하기는 어렵기 때문에 최대한 부수 효과가 발생하는 함수의 양을 최대한 줄일 수 있도록 해야 합니다.

 

*함수를 예측 가능하고 순수하게 유지할 수 있도록 노력해야 하며, 불가피한 부수 효과가 발생과 비순수 함수의 경우 함수명에 해당 함수가 부수 효과를 발생시키는 것을 보다 명확하게 해주어야 합니다.

 

 

 

 

팩토리 함수

팩토리 함수는 기본적으로 또 다른 함수를 제공하는 함수입니다. 

 

// 금액과 세율을 전달받아 반환하는 함수 (세금 계산)
function calculateTax(amount, tax) {
  return amount * tax;
}

// 부가가치세
const vatAmount = calculateTax(100, 0.19);

// 소득세
const incomeTax = calculateTax(100, 0.25);

함수 calculateTax는 인수로 금액과 세율을 전달받아 곱하여 반환하는 세금 계산 함수입니다. 해당 함수를 이용하여 부가가치세, 소득세 상수에 반환받은 값을 할당해 주었습니다.

 

하지만 현재 다른 부분에서 amount가 필요한 경우가 발생하였습니다. 

function createTaxCalculator(tax) {
  function calculateTax(amount) {
    return amount * tax;
  }

  return calculateTax;
}

이때 tax(세율)와 함께 함수를 호출해주어야 합니다. 함수 createTaxCalculator를 만들어 기존에 있던 함수 calculateTax를 감싸주었으며, 함수 createTaxCalculator는 인수로 세율을 전달받고  함수 calculateTax를 반환합니다.

 

// 부가가치세
const vatAmount = createTaxCalculator(0.19);

// 소득세
const incomeTaxAmount = createTaxCalculator(0.25);

console.log(vatAmount(100)); // 19 출력
console.log(incomeTaxAmount(100)); // 25 출력

기존의 값을 할당받던 부가가치세와 소득세는 반환하는 함수 calculateTax를 할당받았기 때문에 이제 함수가 되었으며, 각각의 함수 calculateTax에 인수를 전달해 주었습니다. 이제 세금 계산을 하는 함수는 위의 코드처럼 부가 가치 세율과, 소득 세율을 미리 설정해놓았기 때문에 금액 전달만으로도 원하는 결과를 확인할 수 있습니다. 

 

이것이 팩토리 함수의 개념이고, 팩토리 함수를 이용하여 세율을 한 번 설정해두었기 때문에 해당 세율을 계속 유지하는 경우 금액만 변경하여 사용이 가능해지게 된 것입니다. 팩토리 함수를 사용한다면 시간과 코드가 절약되는 효과를 확인할 수 있을 것입니다.

 

 

 

Closure

클로저(Closure) 클로저(Closure)란 함수가 선언될 때 해당 함수가 유효하게 동작할 수 있는 범위(렉시컬 범위)를 기억하고 있다가, 함수가 외부에서 호출될 때 그 유효 범위의 특정 변수를 참조할 수

nicehyun12.tistory.com

*앞서 게시한 클로저의 경우 팩토리 함수와 밀접한 개념을 가지고 있습니다.

 

 

호이스팅(Hoisting)

호이스팅이란 함수 선언부가 유효범위 최상단으로 끌어올려지는 현상을 말합니다.

 

hello(); // Hello~ 출력

function hello() {
  console.log("Hello~");
}

함수 선언부를 함수 호출부 아래로 옮겨 코드를 실행시키면 함수가 유효범위 최상단으로 끌어올려져 함수가 정상적으로 호출되는 것을 확인할 수 있습니다.

 

 

hello(); // 레퍼런스 애러 발생

const hello = function () {
  console.log("Hello~");
};

이러한 호이스팅은 함수 선언문에서만 발생하고 함수 표현에서는 발생하지 않기 때문에 레퍼런스 에러가 발생하는 것을 확인할 수 있습니다.

 

*hello라는 함수가 초기화되기 전에 접근하여 호출하는 것이 불가능하다는 에러를 확인할 수 있습니다.

 

 

함수 선언부는 일반적으로 위와 같이 간단한 코드가 있는 것이 아닌 복잡한 로직으로 구성되어 있기 때문에 작성한 코드의 위쪽에 있게 된다면 코드를 읽어 나갈 때 복잡한 함수 선언부를 먼저 해석한 뒤 함수 호출부로 넘어가 확인하게 될 것입니다.

 

물론 이것이 문제가 되는 것은 아니지만 함수 선언 시 적절한 추상화를 통해 함수 이름을 지어주기 때문에 함수 이름의 확인만으로도 해당 함수가 어떤 기능으로 동작하는지 대략 적으로 알 수 있기 때문에 복잡한 함수 선언부를 먼저 확인할 필요가 없습니다. 코드를 빠르게 이해할 수 있는 것이 중요할 수 있기 때문에 함수의 선언부를 아래 작성하여 필요시 자세한 코드는 나중에 확인할 수 있도록 만들어 주는 것이 좋을 수 있습니다.

 

*할당 연사자를 사용해 함수를 변수에 할당할 경우 기명함수, 임명함수 상관없이 함수 표현식이기 때문에 호이스팅을 이용할 수 없으므로 그에 맞게 코드를 작성해주어야 합니다. 

 

 

 

 

함수의 반환 및 종료
const hello = () => {
  return "Hello~";
};

console.log(hello()); // Hello~ 출력

함수 내부에 return을 통해 데이터를 반환할 경우 함수가 호출될 경우 호출에 대한 결과로 데이터가 반환되게 됩니다.

 

return의 경우 데이터를 반환하는 역할뿐 아니라 함수를 멈추는 역할도 합니다. return 키워드 뒤에 코드는 동작하지 않고 함수의 실행은 종료됩니다.

 

*return 뒤에 반환할 데이터를 명시하지 않으면 undefined가 기본값이 됩니다.

 

 

 

const plus = (num) => {
  return num + 1;
};

console.log(plus(2)); // 3 출력
console.log(plus()); // NaN 출력

함수 plus는 매개변수 num에 인수로 전달받는 숫자에 1을 더하여 반환합니다. 이때 해당 함수를 잘못 사용하여 인수를 전달하지 않고 호출한 경우 매개변수에 값이 전달되지 않았기 때문에 num은 undefined가 되고 NaN이라는 결과가 반환됩니다.

 

 

const plus = (num) => {
  if (typeof num !== "number") {
    console.log("숫자를 입력해주세요");
    return 0;
  }
  return num + 1;
};

console.log(plus(2)); // 2 출력
console.log(plus()); // 숫자를 입력해주세요 + 0 출력

때문에 함수를 잘못 사용하는 경우를 대비해서 함수 내부에 조건을 추가해 전달받는 인수가 숫자가 아닐 경우 콘솔 출력과 0을 반환하도록 해주었습니다. 

 

 

 

매개변수 패턴(parmeter patten)

[기본값 설정]

const sum = (a, b) => {
  return a + b;
};

console.log(sum(1, 2)); // 3 출력
console.log(sum(7)); // NaN 출력

함수 sum은 매개변수 a와 b를 더해 반환하는 함수이기 때문에 숫자인 인수를 함수에 전달해주어야 합니다. 이때 해당 함수를 잘못 사용하여 인수를 하나만 전달할 경우 매개변수 b는 아무런 값도 전달받지 못했기 때문에 undefined 되고 결과는 NaN이 반환됩니다.

 

 

const sum = (a, b = 1) => {
  return a + b;
};

console.log(sum(1, 2)); // 3 출력
console.log(sum(7)); // 8 출력

이때 매개변수의 인수를 전달받지 못하면 기본적으로 사용할 기본값을 설정해줄 수 있습니다.

 

*기본값의 경우 undefined의 경우만 적용되고 이외의 falsy 값에는 적용되지 않습니다.

 

 

[구조 분해 할당]

const user = {
  name: "rati",
  age: 5,
};

const getName = (user) => {
  return user.name;
};

console.log(getName(user)); // rati 출력

함수 getName은 매개변수에 객체를 전달받아서 전달받은 객체의 name 속성을 반환하고 있습니다.

 

const user = {
  name: "rati",
  age: 5,
};

const getName = (user) => {
  const { name } = user;
  return name;
};

console.log(getName(user)); // rati 출력

이미 함수의 매개변수에 객체를 전달받는 것을 알고 있고, 해당 객체에서 name 속성을 찾아 반환할 것이기 때문에 객체 구조 분해 할당을 통해 값을 반환할 수 있습니다.

 

const user = {
  name: "rati",
  age: 5,
};

const getName = ({ name }) => {
  return name;
};

console.log(getName(user)); // rati 출력

함수 내부에서 전달받은 객체를 구조 분해 할당하는 것에서 조금 더 나아가 매개변수의 자리에서 구조 분해를 진행할 수 있습니다. 

 

함수 호출 시 인수로 객체가 전달되고, 해당 함수의 매개변수가 객체를 받는데, 전달받는 객체를 바로 구조 분해해서 name 속성을 꺼내어 사용하는 것입니다.

 

 

const user = {
  name: "rati",
  age: 5,
};

const getEmail = ({ email }) => {
  return email;
};

console.log(getEmail(user)); // undefined 출력

동일한 방법으로 함수 getEmail을 만들었으며, 해당 함수는 인수로 전달받은 객체에서 email 속성을 찾아 반환합니다.

 

전달받은 객체에 email 속성이 없기 때문에 해당 함수가 반환하는 값은 undefined가 됩니다.

 

const user = {
  name: "rati",
  age: 5,
};

const getEmail = ({ email = "email이 없습니다." }) => {
  return email;
};

console.log(getEmail(user));// email이 없습니다. 출력

구조 분해를 사용한 경우도 기본값 설정이 가능합니다.

 

 

const num = [11, 22, 33];

const getSecondNum = ([, b]) => {
  return b;
};

console.log(getSecondNum(num)); // 22 출력

함수 getSecondNum의 경우 인수로 전달받은 배열의 1번 인덱스에 해당하는 값을 반환합니다.

 

해당 함수는 인수로 전달받는 함수의 1번 인덱스 이외의 값은 사용하지 않기 때문에 ' , '를 사용하여 1번 인덱스에 위치했다고 명시만 해주고 필요하지 않은 값에 대해서 변수 설정을 해주지 않고 필요한 값만 반환이 가능합니다.

 

 

[나머지 매개변수]

const sum = (...rest) => {
  console.log(rest);
};

console.log(sum(1, 2)); // [1,2] 출력
console.log(sum(1, 2, 3, 4)); // [1, 2, 3, 4] 출력
console.log(sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 출력

함수 sum은 인수로 숫자를 전달받으며, 매개변수에 전개 연산자를 사용하여 전달받는 인수를 배열로 반환합니다.

 

const sum = (a, b, ...rest) => {
  console.log(rest);
};

console.log(sum(1, 2)); // [] 출력
console.log(sum(1, 2, 3, 4)); // [3, 4] 출력
console.log(sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); // [3, 4, 5, 6, 7, 8, 9, 10] 출력

만약 전개 연산자 앞에 a와 b 매개변수를 설정해주면 전달받은 첫 번째 인수와 두 번째 인수는 각각 a와 b에 전달되고, 전달되지 못한 나머지 인수는 rest 매개변수에 전달됩니다.

 

 

 

 

즉시실행함수(IIFE : Immediately-Invoked Function Expression)
const a = 7;

const double = () => {
  console.log(a * 2);
};

double();

함수 double은 상수 a에 할당된 값에 2를 곱해서 콘솔에 출력하는 함수이며, 해당 함수를 실행하기 위해 아래에서 호출해 주었습니다.

 

즉시실행함수를 사용할 경우 위의 경우처럼 해당함수를 호출할 필요 없이 함수를 만들자 말자 사용이 가능합니다.

 

const a = 7;

(() => {
  console.log(a * 2);
})(); // 14출력

함수를 만들 때 호출하기 위해 함수에 이름을 지어주게 되는데 함수의 호출 없이 만들자 말자 함수가 실행되기 원한다면 위의 즉시실행함수 문법을 사용하여 바로 실행할 수 있습니다.

 

 

[즉시실행함수 사용 패턴]

 

즉시실행함수 패턴은 아래의 5가지가 있습니다.

(() => {})(); // 1
(function () {})(); // 2
(function () {}()); // 3
!function () {}(); // 4
+function () {}(); // 5

첫 번째 사용패턴의 경우 첫 번째 소괄호 내부에서 화살표 함수를 작성합니다. 화살표 함수의 경우 사용가능한 패턴이 한 가지뿐입니다.

 

두 번째 사용패턴의 경우 첫 번째 사용 패턴과 동일하게 첫 번째 소괄호 내부에서 함수를 작성합니다.

 

세 번째 사용패턴의 경우 첫 번째 소괄호 안에 두 번째 소괄호가 들어가고 두 번째 소괄호 앞에 함수를 작성합니다.

 

네 번째 사용패턴의 경우! 와 소괄호 사이에 함수를 작성합니다. 

 

다섯 번째 사용패턴의 경우 +와 소괄호 사이에 함수를 작성합니다.

 

 

((a, b) => {
  console.log(a);
  console.log(b);
})(1, 2);

즉시실행함수의 첫 번째 소괄호에 함수를 작성하여 해당 함수는 만들어지자 말자 실행됩니다. 이때 해당 함수는 두 개의 인수를 전달받고 각각 콘솔로 출력하는 함수입니다.

 

두 번째 소괄호에 해당 함수에 전달할 인수를 넣어주게 되면 즉시실행함수는 전달받은 인수를 각각 콘솔로 출력하게 됩니다.

 

*즉시실행함수의 대표적인 사용 사례로 전달받는 인수를 실행함수에서 유추할 수 없는 변수를 사용하는 코드의 난독화가 있습니다.

 

 

 

 

콜백함수(Callback)
const a = () => {
  console.log("A");
};

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

a(b); // A 출력

콜백함수란 함수 a에 인수로 함수 b를 전달할 경우 함수 b를 콜백 함수라고 하며, 함수가 실행될 때 인수로 전달되는 함수를 콜백 함수라고 합니다.

 

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

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

a(b); 
// A 출력
// B 출력

함수 a가 실행될 때 인수로 전달받은 함수 b가 실행되어야 하기 때문에 함수 a는 매개변수로 전달받고 내부에서 매개변수를 호출합니다.

 

*함수 a의 매개변수는 인수로 함수를 전달받기 때문에 함수가 되고 기존의 함수 호출처럼 호출하여 실행합니다.

 

 

const sum = (a, b) => {
  setTimeout(() => {
    return a + b;
  }, 1000);
};

console.log(sum(1, 2));
console.log(sum(3, 7));

함수 sum은 매개변수 a, b에 각각의 숫자를 인수로 전달받아 1초 뒤에 반환하는 함수입니다. 하지만 해당 함수는 setTimeout 함수 안에서 연산한 값을 반환하고 있기 때문에 해당 함수는 값을 반환하지 않아 아래의 콘솔의 결과는 undefined가 됩니다. 이때 콜백함수를 사용하면 값을 정상적으로 반환할 수 있습니다.

 

const sum = (a, b, c) => {
  setTimeout(() => {
    c(a + b);
  }, 1000);
};

console.log(
  sum(1, 2, (value) => {
    console.log(value);
  })
);

함수 sum에 추가로 매개변수 c를 만들어주고 해당 매개변수에 콜백함수를 전달합니다. 전달받은 콜백함수는 setTimeout 함수에서 호출을 해주고 해당 콜배함수에 연산된 값을 넣어줍니다. 이때 전달된 콜백함수는 매개변수 value에 연산된 결과를 전달받아 콘솔로 출력하기 때문에 3이 콘솔에 출력됩니다.

 

 

 

 

함수의 재귀(Recursive)
const a = () => {
  console.log("A");
  a();
};

a();

함수 호출을 해당 함수 안에서 호출하는 것을 함수 재귀라고 합니다. 위의 코드의 경우 최초의 함수 호출에 의해 함수가 실행되고 해당 함수 내부에서 자신을 호출하기 때문에 무한으로 함수가 실행되게 됩니다.

 

let i = 0;

const a = () => {
  console.log("A");
  i += 1;

  if (i < 4) {
    a();
  }
};

a();

해당 함수가 무한으로 실행되기 때문에 해당 함수가 한 번 실행될 때마다 변수 i의 값을 1씩 증가하도록 해주었고, i의 값이 4보다 작을 경우만 함수의 재귀 호출하도록 해주었습니다.

 

*함수의 재귀 호출의 경우 무한으로 실행되기 때문에 해당 함수가 멈출 수 있는 조건을 추가해주어야 합니다.

 

 

const userA = {
  name: "A",
  parent: null,
};

const userB = {
  name: "B",
  parent: userA,
};

const userC = {
  name: "C",
  parent: userB,
};

const userD = {
  name: "D",
  parent: userC,
};

const getRootUser = (user) => {
  if (user.parent) {
    return getRootUser(user.parent);
  }

  return user;
};

console.log(getRootUser(userD)); // {name: 'A', parent: null} 출력

각각의 객체 데이터는 parent 속성에 다른 객체를 참조하고 있습니다. 함수 getRootUser의 경우 인수로 전달받는 객체의 parent 속성의 값이 truthy일 경우에 함수 재귀 호출을 합니다. 때문에 해당 함수는 인수로 전달받는 객체의 parent 속성의 null 경우 함수의 재귀 호출을 멈추고 콘솔을 출력하게 됩니다. 

 

 

 

함수의 재귀를 이용하면 코드의 길이를 줄일수 있는 장점과 for 반복문으로 해결할 수 없었던 문제도 해결할 수 있게 됩니다.

const myself = {
  name: "rati",
  friends: [{ name: "merry", friends: [{ name: "lewis" }] }, { name: "julia" }],
};

rati는 이름이 merry인 친구와 julia인 친구가 있습니다. merry는 이름이 lewis 친구가 있고 lewis와 julia는 친구가 없습니다.

 

위의 코드는 동일한 구조를 가진 객체가 여러 번 사용되었고 중접 구조로 다소 복잡해 보입니다. lewis와 julia가 친구가 있다면 보다 복잡해 보일 수 있습니다. 이러한 중첩된 데이터를 실제로 접할 일은 생각보다 많을 것입니다.

 

해당 객체의 친구 이름을 모두 출력하려고 하는데 위의 객체의 경우 for 반복문을 사용하여 모든 친구 이름을 출력할 수 없습니다. lewis와 julia가 현재는 친구가 없지만 나중에는 친구가 있을 수 도 있으며, 해당 객체의 중첩 레벨을 알고 있다고 해도 많은 반복문을 사용하여 출력을 해야 할 것입니다.

 

function printFriendNames(person) {
  const collectedNames = [];

  for (const friend of person.friends) {
    collectedNames.push(friend.name);
  }

  return collectedNames;
}

console.log(printFriendNames(myself)); // ['merry', 'julia'] 출력

위의 코드로 함수를 실행시켜 친구의 이름을 출력할 경우 merry의 친구 이름은 출력되지 않고 직속 친구의 이름만 출력됩니다. 

 

const myself = {
  name: "rati",
  friends: [{ name: "merry", friends: [{ name: "lewis" }] }, { name: "julia" }],
};

function printFriendNames(person) {
  const collectedNames = [];

  if (!person.friends) {
    return [];
  }

  for (const friend of person.friends) {
    collectedNames.push(friend.name);
    collectedNames.push(...printFriendNames(friend));
  }

  return collectedNames;
}

console.log(printFriendNames(myself));// ['merry', 'lewis', 'julia'] 출력

위와 같이 함수 안 반복문에서 함수의 재귀를 사용하여 친구의 친구를 찾았으며 앞서 언급한대로 재귀 함수가 무한으로 실행되지 못하게 친구가 없으면 빈배열을 반환하도록 해주었습니다. 위의 예시처럼 재귀 함수를 사용해 보다 적은 코드길이와 반복문으로 해결할 수 없었던 중첩 객체의 속성을 사용할 수 있게 되었습니다.

 

 

 

호출 스케쥴링

호출 스케쥴링은 함수의 호출을 지연하거나 반복적으로 호출할 수 있도록 만들어줍니다. 

 

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

setTimeout(hello, 2000); // 2초 뒤 hello~ 출력

대표적으로 setTimeout 함수를 사용하여 특정 함수의 실행을 지연할 수 있습니다.

 

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

const timeout = setTimeout(hello, 2000);
const h1El = document.querySelector("h1");

h1El.addEventListener("click", () => {
  clearTimeout(timeout);
});

기존의 setTimeout 함수를 사용하여 함수 hello가 2초 뒤 실행되도록 스케줄을 잡은 다음 h1 요소를 클릭하면 타이머를 제거해 줍니다.

 

2초가 지나기 전에 h1을 클릭하게 되면 잡혀있던 함수의 스케줄은 제거되어 함수 실행이 되지 않습니다.

 

 

*setInterval 함수를 사용하여 설정한 시간에 한 번씩 계속 함수가 실행되게 스케쥴링을 하는 경우 clearInterval을 통해 스케줄을 제거할 수 있습니다.

 

 

 

this
  • 일반 함수의 this는 호출 위치에서 정의
  • 화살표 함수의 this는 자신이 선언된 함수(렉시컬) 범위에서 정의
const user = {
  firstName: "rati",
  lastName: "Lee",
  age: 5,
  getFullName: function () {

    return `${this.firstName} ${this.lastName}`;
  },
};

console.log(user.getFullName()); // rati Lee 출력

위의 코드에서 this는 getFullName이라는 속성이 들어있는 객체 데이터를 의미합니다. 

 

즉 하단의 콘솔 로그에서 객체 user에서 getFullName 속성을 호출했기 때문에 this는 user가 되는 것입니다.

 

 

const user = {
  firstName: "rati",
  lastName: "Lee",
  age: 5,
  getFullName: () => {
    return `${this.firstName} ${this.lastName}`;
  },
};

console.log(user.getFullName()); // undefined undefined 출력

만약 this를 일반함수가 아닌 화살표 함수 내부에서 사용하게 된다면 위의 결과가 나옵니다.

 

이는 화살표 함수에서 this를 사용할 경우 자신이 호출된 함수(화살표 함수)를 감싼 함수에서 firstName과 lastName을 찾기 때문입니다.

하지만 현재 화살표 함수를 감싸고 있는 함수가 없기 때문에 firstName과 lastName 찾지 못하게 됩니다.

 

function user() {
  firstName = "merry";
  lastName = "part";

  return {
    firstName: "rati",
    lastName: "Lee",
    age: 5,
    getFullName: () => {
      return `${this.firstName} ${this.lastName}`;
    },
  };
}

const u = user();
console.log(u.getFullName()); // merry part 출력

이번에는 화살표 함수를 다른 함수로 감싸주고 감싼 함수 내부에 this 키워드를 사용해 값을 할당해 주었습니다. 이제 화살표 함수에서 사용된 this는 자신이 선언된 함수를 감싸는 함수 내부의 firstName과 lastName을 찾아 사용할 수 있게 됩니다.

 

 

function user() {
  firstName = "merry";
  lastName = "part";

  return {
    firstName: "rati",
    lastName: "Lee",
    age: 5,
    getFullName: function () {
      return `${this.firstName} ${this.lastName}`;
    },
  };
}

const u = user();
console.log(u.getFullName()); // rati Lee 출력

기존의 getFullName 속성의 화살표 함수를 일반 함수로 변경해주게 되면 this는 자신의 호출 위치가 getFullName이 있는 객체이기 때문에 객체 내부에 있는 firstName과 lastName을 찾아 사용하게 됩니다.

 

 

*객체 내부에 축약형을 사용하여 함수를 선언할 경우 일반 함수에 해당합니다.

 

 

function user() {
  firstName = "merry";
  lastName = "part";

  return {
    firstName: "rati",
    lastName: "Lee",
    age: 5,
    getFullName() {
      return `${this.firstName} ${this.lastName}`;
    },
  };
}

const lewis = {
  firstName: "Lewis",
  lastName: "Yang",
};

const u = user();
console.log(u.getFullName.call(lewis)); // Lewis Yang 출력

메서드 getFullName이 없는 객체 lewis를 생성해주고 call 메서드를 사용하여 함수 user에 있는 메서드 getFullName를 빌려 사용하는 경우 this를 호출하는 대상은 더 이상 user가 아니고 lewis이기 때문에 해당 객체에서 firstName과 lastName을 찾아 사용하게 됩니다.

 

 

'JavaScript' 카테고리의 다른 글

Testing  (0) 2022.12.28
HTTP  (0) 2022.12.25
Closure  (0) 2022.12.25
클래스  (0) 2022.12.24
정규표현식  (0) 2022.12.24