HyunJun 기술 블로그

JS의 Function, This와 ES6 본문

JavaScript

JS의 Function, This와 ES6

공부 좋아 2023. 7. 28. 08:46
728x90
반응형

1. JavaScript의 This

기본적으로 JavaScript의 this는 함수가 호출될 때 해당 함수가 속한 객체를 참조하는 특별한 키워드이다. this를 사용하여 현재 실행 중인 함수가 속한 객체의 프로퍼티에 접근하거나 해당 객체의 메서드를 호출할 수 있다. 즉 this에 대한 값은 호출 방법에 따라 동적으로 결정된다.

 

1) function 생성 방법.

시작하기 전, 함수의 생성 방법부터 알아보자면, ES6에서 나온 Arrow Function(화살표 함수)는 일단 제쳐두고, 함수를 선언하는 데 있어 함수 선언식과, 표현식이라는 2가지 방법이 존재한다.

  foo(); // 함수 선언식
  function foo() {
    console.log("함수 선언식");
  }

  bar(); // Uncaught ReferenceError: Cannot access 'bar' before initialization
  const bar = function () {
    console.log("함수 표현식");
  };

함수 선언식과 표현식의 차이

위의 결과에서도 볼 수 있듯이, 함수 선언식은 호이스팅에 의해 스코프의 상단으로 끌어올려진다. 함수 표현식은 함수를 변수에 할당하는 방식으로 함수를 정의한다. 함수 표현식은 변수에 할당된 이후에 사용 가능하며, 호이스팅은 변수 선언만 끌어올리기 때문에 함수 할당은 호이스팅 되지 않는다.

 

2) function 사용 방법.

  • 일반 함수로 사용.
  • 생성자 함수로 사용.
  • 객체 메서드 할당.

 

일반 함수로 사용.

  function foo() {
    console.log(this); // Window 객체
  }

  const a = foo();

함수를 단순히 호출하게 되면 기본적으로 this는 Window 객체를 가리킨다. 함수를 호출할 때 this가 현재 실행 문맥(context)에서 해당 함수가 속한 객체를 가리키려는 성질이 있다. 그러나 일반 함수의 경우 암시적으로 어떤 객체에 바인딩 되지 않기 때문에 this는 전역 객체인 Window를 가리킨다. 브라우저 환경에서는 일반적으로 전역 객체는 Window 객체이므로 함수가 암시적으로 어떤 객체에도 바인딩 되지 않을 경우 this는 Window 객체를 가리킨다.

생성자 함수로 사용.

  function foo() {
    console.log(this); // foo{}
  }

  const a = new foo();

이때의 this는 foo{}를 가리키게 되는데 그 이유는 자바스크립트는 new 키워드와 함께 함수를 호출하게 되면 아래와 같은 동작이 일어난다.

  function foo() {
    // this = {};
    console.log(this); // foo{}

    // return this;
  }

  const a = new foo();

그 이유는 new를 사용하여 생성자 함수를 사용해서 인스턴스를 생성하는데 목적이 있다. 즉 인스턴스 a는 new foo();를 사용하여, foo{}를 할당한 것이다.

  function foo() {
    this.a = "123"; // window.a => 123

    console.log(window.a);
  }

  const a = foo();

  function bar() {
    this.b = "456"; // bar.b => 456

    console.log(this.b);
  }

  const b = new bar();

 

객체 메서드로 할당.

  function foo() {
    console.log(this); // {method: f foo()}
  }

  const bar = {
    method: foo,
  };

  bar.method();

위의 코드에서 bar 객체의 method 프로퍼티에 할당된 함수 foo를 호출할 때, this는 bar 객체를 가리킨다. 이는 함수를 메서드로서 호출할 때 this가 해당 메서드를 소유한 객체를 가리키는 특성 때문이다. bar.method()를 호출하면, foo 함수가 메서드로서 호출된다. 함수 foo의 내부에서 console.log(this);를 실행하면 this는 bar 객체를 가리키게 된다. 따라서, bar.method()를 호출할 때 this는 {method: f foo()}와 같은 bar 객체를 가리키게 된다.

 

 

2. ES6

ES6에서는 꾀나 많은 부분이 변경되었는데 대표적으로 함수 선언식, 표현식만 해도 호이스팅 결과가 다른데, 함수가 일반 함수로 사용되기도 하고 생성자 함수로 사용되기도 한다. 하여 이러한 복잡성을 해결하고자 순수 함수 사용에 대응되는 ArrowFunction 및 생성자 함수에 대응되는 Class가 나왔다.

  • 생성자 함수: class가 등장하면서, 생성자 함수를 안 쓰고 클래스의 생성자로 인스턴스를 만드는 방법이 생겼다.
  • 객체 메서드 할당, 함수 선언식의 불편한 점들을 보완하고자, 화살표 함수(Arrow Function)가 나왔다.
    • 고로 단순히 함수를 사용할 때에는 Arrow Function 사용을 추천한다. Arrow Function을 new 키워드를 붙여 호출하면 "" is not a constructor 에러가 발생한다. 즉 컨스트럭터가 존재하지 않는다. 또한 화살표 함수는 this를 바인딩 하지 않는다.
  function bar() {}
  const foo = () => {};

  console.dir(bar);
  console.dir(foo);

 

Arrow Function은 함수 선언식과 다르게 constructor를 포함하는 prototype 객체 자체가 존재하지 않는다.

3. this 바인딩

이렇듯 함수 생성 방법과, 함수 사용 방법이 여러 가지이고, 그때마다 this가 어떻게 바인딩 될지 생각하는 게 까다롭다. 하여 JS에서는 this와 관련해서 여러 가지 메서드를 제공한다.

  • bind: 함수를 호출하지 않고, 원본 함수의 this 값을 영구적으로 바인딩 한 새로운 함수를 생성하는 메서드이다. call과 apply와 달리 함수를 실행하지 않고 새로운 함수를 반환하는 점이 차이점이다.
  • call: call 메서드를 사용하여 함수를 호출할 때, 함수의 this 값을 특정 객체로 지정할 수 있다. 이후에는 해당 함수 내에서 this를 사용할 때 지정한 객체가 this로 취급된다.
  • apply: call과 비슷하지만 인자들을 배열 형태로 전달한다.

 

1) call

  const foo = {
    name: "John",
    sayHello: function () {
      console.log(`Hello, ${this.name}!`);
    },
  };

  const bar = {
    name: "Alice",
  };

  foo.sayHello(); // Hello, John!
  foo.sayHello.call(bar); // Hello, Alice!

 

 

2) apply

  const foo = {
    name: "John",
    sayHello: function (greeting) {
      console.log(`${greeting}, ${this.name}!`);
    },
  };

  const bar = {
    name: "Alice",
  };

  foo.sayHello("Hi"); // Hi, John!
  foo.sayHello.call(bar, ["Hello"]); // Hello, Alice!

 

 

3) bind

  const foo = {
    name: "John",
    sayHello: function (greeting) {
      console.log(`Hello, ${this.name}!`);
    },
  };

  const bar = {
    name: "Alice",
  };

  foo.sayHello(); // Hello, John!
  const boundSayHello = foo.sayHello.bind(bar);
  boundSayHello(); // Hello, Alice!

 

또한 bind를 사용하여 함수 인자들을 미리 고정할 수도 있다. bind를 사용하여 add 함수의 첫 번째 인자를 항상 5로 고정한 addFive 함수를 생성했다. 이후 addFive(10)을 호출하면 내부적으로는 add(5, 10)과 같이 동작하여 15를 반환한다.

  function add(a, b) {
    return a + b;
  }

  const addFive = add.bind(null, 5);
  console.log(addFive(10)); // 15
728x90
반응형
Comments