[JavaScript] 헤이 왓 이즈 this ?

hyeonbin·2023년 4월 26일
0

JS 계란반 스터디

목록 보기
7/12
post-thumbnail

📃 this

🔈기본 개념

  • this는 모든 함수 스코프 내에 자동으로 생성되는 특수한 식별자다.

  • tihs는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수다.

  • this의 값은 함수를 호출하는 방법과 호출 시점에 의해 this에 바인딩할 객체가 동적으로 결정된다.


🔈 함수 호출 방식과 this 바인딩

✔️ 바인딩

  • 식별자와 값을 연결하는 과정
    → 변수나 함수와 같은 식별자와 그에 대응하는 값 또는 메모리 주소를 연결하는 것을 의미한다.

  • 변수 선언과 메모리 바인딩
    → 변수선언은 변수이름과 확보된 메모리 공간의 주소를 연결하는 것을 바인딩이라고 한다.

  • this 바인딩
    → 함수가 실행될 때, this 키워드가 어떤 객체를 참조하는지를 결정하는 것을 this 바인딩이라고 한다.


✔️ 전역 컨텍스트에서의 this

  • this는 브러우저 환경에서 window, 서버 환경 ( Node.js )에서 global 전역객체를 참조한다.

  • 브라우저에서 자바스크립트 코드가 실행되면, 전역 스코프에 선언된 모든 변수와 함수는 window 라는 객체의 프로퍼티와 메소드이다.

  • 이제 함수 선언식과 함수 표현식으로 일반함수를 호출하면 this는 전역객체에 바인딩 된다.
  // 함수 선언식
  function globalFun() {
    return this;
  }

  globalFun(); // window 출력

  // 함수 표현식
  const globalFun = function() {
    console.dir(this);
  }

  globalFun(); // window 출력


✔️ 화살표 함수에서의 this

  • 화살표 함수는 함수를 선언할 때 this에 바인딩할 객체가 정적으로 결정된다.

  • 화살표 함수 안에서 this는 언제나 상위 스코프의 this를 가리킨다.

  • 화살표 함수의 this 바인딩 객체 결정 방식은 함수의 상위 스코프를 결정하는 방식인 렉시컬 스코프와 유사하다. 이를 렉시컬 Lexical this 라고 한다.

  • 화살표 함수는 call, apply, bind 메소드를 사용하여 this를 변경할 수 없다.

  // 화살표 함수
  const arrowFun1 = () => {
    console.log(this);
  };

  arrowFun1(); // window 출력

  const obj = {
    arrowFun1: function () {
      const arrowFun2 = () => {
        console.dir(this);
      };
      arrowFun2();
    },
  };

  obj.arrowFun1(); // obj 출력


✔️ 객체 메서드에서의 this

  • 메서드를 호출하면 기본적으로 this는 해당 메서드를 가지고 있는 객체에 바인딩 된다.

  • 메서드가 화살표 함수로 작성되었을 때는 화살표 함수의 this는 상위 컨텍스트의 this를 계승받기 때문에 this가 전역 객체에 바인딩 된다.

  const obj = {
    aaa: function () {
      console.log('aaa this:', this);
    },
    bbb() {
      console.log('bbb this:', this);
    },
    ccc: () => {
      console.log('ccc this:', this);
    },
  };

  obj.aaa(); // aaa this: obj
  obj.bbb(); // bbb this: obj
  obj.ccc(); // ccc this: window


✔️ 함수 내부에서의 this

  • 함수는 전역에 선언된 일반 함수객체 안에 메서드로 크게 구분할 수 있다.

  • 내부 함수가 호출되는 시점에 동적으로 this가 결정되며, 이때 내부 함수가 어떻게 호출되었느냐에 따라 this가 결정되고, 기본적으로는 전역 객체에 바인딩된다.

  • 내부함수의 this가 전역객체를 바인딩하지 않는 방법도 있다.

  const obj = {
    aaa: function () {
      function aaaInner() {
        console.log('aaaInner this:', this);
      }

      aaaInner();
    },
    
	// that 변수에 bbb 메서드의 this를 할당해 사용
    // 그러면 내부함수의 this가 전역객체를 바인딩하지 않음
    bbb: function () {
      const that = this; 
      function bbbInner() {
        console.log('bbbInner this:', that);
      }

      bbbInner();
    },

    ccc: function () {
      const cccInner = () => {
        console.log('cccInner this:', this);
      };

      cccInner();
    },
  };

  obj.aaa(); // aaaInner this: window
  obj.bbb(); // bbbInner this: obj
  obj.ccc(); // cccInner this: obj


✔️ 콜백 함수에서의 this

  • 콜백함수를 호출하면 제어권을 가지는 함수가 this를 무엇으로 바인딩할지 결정한다.
  const obj = {
    aaa: function () {
      // 화살표 함수는 상위 스코프의 this를 바인딩 한다.
      setTimeout(() => {
        console.log('aaa setTimeout this:', this);
      }, 1000);
    },

    bbb: function () {
      setTimeout(function () {
        console.log('bbb setTimeout this:', this);
      }, 1000);
    },
  };

  obj.aaa(); // aaa setTimeout this: aaa
  obj.bbb(); // bbb setTimeout this: Window


✔️ 생성자 함수에서의 this

  • new 키워드로 생성자 함수를 호출하면 빈 객체가 생성되고, 이 빈 객체는 생성자 함수의 프로토타입과 연결된다.

  • 생성자 함수 내에서 사용되는 this는 새로 생성된 빈 객체로 바인딩 되고, this를 통해 새로운 객체에 프로퍼티와 메서드를 추가할 수 있다.

  • 생성자 함수 내의 코드가 실행되면서 새로운 객체가 초기화 된다.

  • new 키워드를 빼먹으면 일반함수 호출과 같아지기에, 이 경우에는 this가 window에 바인딩 된다.

  function Kim(name) {
    this.name = name;
  }

  // 생성자 함수 호출 
  const kim = new Kim('park');

  console.log(kim.name); // park


✔️ apply / call / bind에서의 this

  • call 메소드는 함수를 호출하며, 첫번째 매개변수로 전달된 객체를 함수내에서 this로 사용한다.

  • 추가 매개변수들은 해당 함수에 인자로 전달된다.

  function example(num1, num2) {
    console.log(this.name, num1 + num2);
  }

  const person = {
    name: 'Kim',
  };

  example.call(person, 3, 5); // Kim 8

  • apply 메소드는 call과 유사하지만, 인자들을 배열 형태로 전달한다.

  • 배열의 각 요소가 함수에 순서대로 인자로 전달된다.

  function example(num1, num2) {
    console.log(this.name, num1 * num2);
  }

  const person = {
    name: 'Kim',
  };

  example.apply(person, [4, 6]); // Kim 24

  • bind 메소드는 함수를 호출하지 않고, 새로운 함수를 반환한다.

  • 반환된 함수는 전달된 첫번째 매개변수를 this로 바인딩한 상태로 유지한다.

  function example(num1, num2) {
    console.log(this.name, num1 - num2);
  }

  const person = {
    name: 'Kim',
  };

  const newExample = example.bind(person);
  newExample(20, 10); // kim 10


✔️ 이벤트 핸들러 안에서의 this

  • 이벤트 핸들러에서 this는 이벤트를 받는 HTML 요소를 가리킨다.
  <button id="btn">click me!</button>
  const btn = document.querySelector('#btn');

  btn.addEventListener('click', function () {
    console.log(this); // this는 클릭된 button을 가리킴
  });


✔️ 체이닝

  • 함수에서 자기 자신 this를 리턴하면 자기객체를 가리키기 때문에 연속으로 . 을 사용할 수 있다.
  let stairs = {
    step: 0,
    up() {
      this.step++;
      return this;
    },
    down() {
      this.step--;
      return this;
    },
    showStep() {
      alert( this.step );
    }
  }

  stairs.up().up().up().down().up().up().down().showStep(); // 출력: 3

간단한 코드 설명
  1. stairs.up()을 호출하면, this는 stairs 객체를 가리키고 step을 1 증가시킨다.
  1. .down()을 호출하면, this는 여전히 stairs 객체를 가리키고 step을 1 감소시킨다.
  1. .showStep()을 호출하면 this는 여전히 stairs 객체를 가리키니까 step 값을 alert로 출력한다.
  1. 여기서는 전부 객체의 메소드를 호출하니까, this는 그 메소드가 속해있는 객체를 가리키고, up() 메소드 5번, down() 메소드 2번, step 값 즉, 출력된 값은 3이다.

profile
할 수 있다고 믿는 사람은 결국 그렇게 된다 😄😊

0개의 댓글