기록

JavaScript Engine - 3. this 본문

JS/etc

JavaScript Engine - 3. this

mnmhbbb 2022. 3. 23. 00:01

this

this는 함수가 호출될 때 결정된다! -> 비동기에서도 마찬가지
this는 함수가 호출될 때 호출 방식에 따라 정해진다.(동적)

1. JS에서 this는 기본적으로 window이다.(브라우저에서)
- window가 아닌 경우는 다음 2~4번과 같다.
- Node.js에서는 global
- 최근에는 브라우저, 노드 모두 globalThis로 통합됨

console.log(this); // window

function a() {
  'use strict';
  console.log(this);
}
a(); // undefined

'use strict'; 가 있으면 window가 아닌 undefined

 

2. 객체의 메서드가 호출될 때, 해당 메서드를 호출한 객체에 바인딩
- 단, 반드시 .앞 객체에 바인딩된다고 단언할 수만은 없다.
- 아래 예시처럼 변수에 할당하는 경우는 다른 결과가 나온다.

const obj = {
  name: 'mina',
  sayName() {
    console.log(this.name);
  },
};

obj.sayName(); // this = obj

const sayN = obj.sayName;
sayN(); // =window.name
// 메서드를 꺼내서 변수에 대입하고 호출하면, 이 경우는 this가 window니까
// 즉, 함수가 호출될 때마다, 호출되는 방법에 따라 달라지는 것

 

3. 화살표 함수는 상위 스코프(부모 스코프)의 this를 가리킨다.
즉, 선언할 때 this에 바인딩될 객체가 결정됨(정적)
코드를 작성하는 순간 정해짐

const obj = {
  name: 'mina',
  sayName: () => {
    console.log(this.name);
  },
};
obj.sayName(); // this = window

 

4. 함수에 new를 붙여서 호출할 때(생성자함수)

이때 this는 앞으로 생성될 인스턴스 자체를 가리킴

 

 

5. bind, call, apply로 this에 바인딩할 객체를 변경할 수 있다.

function sayName() {
  console.log(this.name);
}
sayName(); // window.name 
sayName.bind({ name: 'mina' })(); // mina
또는
sayName.call({ name: 'mina' }); // mina
  • bind는 함수를 선언하는 거라서 호출을 추가로 해줘야 함. ()
  • call, apply는 함수를 호출하는 메서드
  • call(this에 바인딩할 객체, 넘길 인자)
  • call: 하나의 인자를 전달
  • apply: 인자를 배열(Array)로 묶어서 전달
function sayName() {
  console.log(this.name);
}
sayName(); // window.name 
sayName.apply({ name: 'mina' }); // mina 
sayName.call({ name: 'mina' }); // mina

 

 

화살표함수

  • 예시 1
    함수선언식+함수선언식
const obj = {
  name: 'mina',
  sayName() {
    console.log(this.name);
    function inner() {
      console.log(this.name);
    }
    inner();
  },
};

obj.sayName(); // mina window.name

// scopechain: inner의 부모는 -> sayName의 부모는 -> anonymous



  • 예시 2
    화살표함수+함수선언식
const obj = {
  name: 'mina',
  sayName: () => {
    console.log(this.name);
    function inner() {
      console.log(this.name);
    }
    inner();
  },
};
obj.sayName(); // window.name window.name



  • 예시 3
    함수선언식+화살표함수
const obj = {
  name: 'mina',
  sayName() {
    console.log(this.name);
    const inner = () => {
      console.log(this.name);
    };
    inner();
  },
};
obj.sayName(); // mina mina
  • this는 함수가 호출될 때 정해진다!!
  • obj.sayName() 즉, this에 바인딩되는 객체는 obj
  • 화살표 함수의 this는 상위 스코프(sayName)의 this에 바인딩 되기 때문에
  • sayName() 메서드의 this.name은, 호출될 때 결정된 'mina'
  • 따라서 inner 스코프의 this.name 또한 'mina'가 됨
  • 스코프에 집중하면 간단하게 알 수 있는 내용
  • this가 호출될 때 결정되니까 호출스택을 그릴 줄 알아야 함
// 호출스택
log: mina
inner: this=obj
log: mina
obj.sayName: this=obj
anonymous: this=window



  • 예시 4
    함수선언식+함수선언식+객체바인딩
const obj = {
  name: 'mina',
  sayName() {
    console.log(this.name);
    function inner() {
      console.log(this.name);
    }
    // inner() // window.name
    inner.call(obj); // 객체 바인딩
  },
};
obj.sayName(); // mina mina

 

 

 

이벤트리스너에서 this

const header = document.querySelecor('#header');
header.addEventListener('click', function () {
  console.log(this);
});
// <h1 id="header">헤더</h1>
  • addEventListener 함수를 호출
  • 그리고 그 안에 함수 선언을 인자로 담은 것(호출하는 것이 아님)
  • 그러면 이 함수 선언을 어디에서 호출하는지 분석할 수가 없음
  • addEventListener 안에서 어떻게 실행되는지를 알 수 없음
  • 따라서 this가 호출되는 방식을 외워야 함!
  • 어떤 방식으로 this가 결정되는지 알 수 없기 때문
  • addEventListener함수로 호출할 경우 this는 header 자리에 오는 객체이다.
  • A.addEventListener() 에서 this는 A라고 암기
  • 내부에서 어떻게 동작하는지는 알 수 없기 때문에 정확하게 몰라도 됨
  • 사실 addEventListener는 c++로 만들어지는데, addEventListener 내부에서 어떻게 실행되는지 js로 추측해보자.
const header = {
  addEventListener: function (eventName, callback) {
    //   callback(); // this가 window이므로 틀림
    callback.call(header); // this가 header
    callback.call(this); // 이렇게 작성해도 동일함
    // 어쨌든 this에 이렇게 바인딩할거라는 것
  },
};

header.addEventListener('click', function () {
  console.log(this); // header
});
  • 즉, obj.method(callback)과 같은 경우에서 callback에 this가 어떤 객체일지는 아무도 모른다! 왜냐면 this는 함수가 호출될 때 동적으로 결정되기 때문
  • 만약 함수 선언을 화살표 함수로 한다면?
    선언할 때 정적으로 this 바인딩 객체가 결정된다.(상위 스코프)
const header = document.querySelecor('#header');
header.addEventListener('click', () => {
  console.log(this);
});
// window

const header = {
  addEventListener: function (eventName, callback) {
    callback(); // 화살표함수에서는 그냥 즉시 실행됨. call 붙일 수 없음. 선언할 때 this가 결정되므로.
  },
};

 

출처: https://youtu.be/0yJsvly3nuk

 

Comments