[Javascript] this 복습

this 개념

- 자신이 속한 객체, 자신이 생성할 인스턴스를 가리키는 식별자 (자기 참조 변수)

- this 바인딩은 함수 호출 방식에 따라 동적으로 결정된다.

- 자바스크립트 엔진이 암묵적으로 생성함

 

호출 방식에 따른 this 바인딩

1. 객체 리터럴메서드를 호출한 객체를 가리킨다

const circle = {
  radius: 5,
  getDiameter() {
    return 2 * this.radius;
  }
}

console.log(circle.getDiameyer()); // 10

 

2. 생성자 함수 생성자 함수가 생성할 인스턴스를 가리킨다

function Circle(radius) {
  this.radius = radius;
}

Circle.prototype.getDiameter = function() {
  return 2 * this.radius;
}

const circle1 = new Circle(5);
console.log(circle1.getDiameter()); // 10

const circle2 = new Circle(100);
console.log(circle2.getDiameter()); // 200

 

3. 기타

  - 전역 → 전역 객체(window)

  - 일반 함수 → 전역 객체(window)

  - strict mode 일반 함수 → undefined

// 1. 전역
console.log(this); // window

// 2. 일반 함수
function square(number) {
  console.log(this); // window
  return number * number;
}
square(2)

// 3. strict mode 에서의 일반 함수
'use strict';

function square2(number) {
  console.log(this); // undefined
  return number * number;
}

square2(2)

 

함수 호출 방식에 따른 this 바인딩

🚨 렉시컬 스코프와 this 바인딩은 결정 시기가 다르다.

- 렉시컬 스코프: 함수 정의가 평가되어 함수 객체가 생성되는 시점에 결정됨 (함수의 상위 스코프를 결정)
- this 바인딩: 함수 호출 시점에 결정됨 (this 바인딩을 결정)

 

1. 일반 함수 호출

  - 일반적으로는 전역객체가 바인딩 된다.

  - strict mode에서는 undefined가 바인딩 된다. (객체를 생성하지 않는 일반 함수에서는 this가 의미없기 때문)

  - 어떠한 함수라도 일반 함수로 호출되면 this에 전역 객체가 바인딩 된다. (중첩 함수, 콜백 함수 포함)

var value = 1;

const obj = {
  value: 100,
  foo() {
    console.log(this); // {value:100, foo:f}
    
    // 메서드 내의 중첩 함수 정의
    function bar() {
      console.log(this); // window
      console.log(this.value); // 1
    }
    
    // 중첩 함수 호출 -> 이 시점에 this 바인딩이 일어남 -> bar 내부의 this에는 전역객체 바인딩
    bar();
    
    setTimeout(function() {
      console.log(this); // window
      console.log(this.value); // 1
    }, 100);
  }
}

obj.foo();

 

2. 메서드 호출

  - 메서드를 호출할 때 마침표(.) 연산자 앞에 기술한 객체가 바인딩 된다.

  - 메서드를 소유한 객체가 아닌 메서드를 호출한 객체에 바인딩 된다.

const person = {
  name: 'KIM',
  // 메서드는 객체에 포함된 것이 아닌 독립적으로 존재하는 별도의 객체임!
  getName() {
    return this.name;
  }
};

console.log(person.getName()); // KIM

 

3. 생성자 함수 호출

  - 생성자 함수가 미래에 생성할 인스턴스가 바인딩 됨

  - new 연산자로 호출해야 생성자 함수로 호출한 것! → 그냥 호출한다면 일반 함수 호출과 같음

function Circle(radius) {
  this.radius = radius;
  this.getDiameter = function() {
    return 2 * this.radius;
  }
}

const circle1 = new Circle(5);
const circle2 = new Circle(10);

console.log(circle1.getDiameter()); // 10
console.log(circle2.getDiameter()); // 20

const circle3 = Circle(15); // 일반 함수 호출
console.log(circle3); // undefined

 

4. apply/call/bind 메서드에 의한 간접 호출

  - this로 사용할 객체와 인수 리스트를 인수로 전달받아 호출 → 첫번째 인수로 전달한 객체가 this가 됨

  - apply, call 메서드는 함수를 호출하는 것!

  - bind는 함수를 호출하지 않고 바인딩이 교체된 함수를 새롭게 생성하여 반환함

function getThisBinding() {
  return this;
}

const thisArg = { a:1 };

console.log(getThisBinding()); // window

// apply는 두번째 인수 전달 시 배열로 묶어 전달
console.log(getThisBinding.apply(thisArg)); // {a:1}
// call는 두번째 인수 전달 시 쉼표로 구분한 리스트 형식으로 전달
console.log(getThisBinding.call(thisArg)); // {a:1}

 

this 바인딩을 일치 시키는 방법

1. Function.prototype.apply / call / bind

- this를 명시적으로 바인딩할 수 있는 메서드를 사용

var value = 1;

const obj = {
  value: 100,
  foo() {
    setTimeout(function() {
      console.log(this.value); // 100
    }.bind(this), 100);
  }
}

obj.foo();

 

2. 화살표 함수 사용

  - 화살표 함수 내부의 this는 상위 스코프의 this를 가리킨다.

var value = 1;

const obj = {
  value: 100,
  foo() {
    setTimeout(() => console.log(this.value), 100); // 100
  }
}

obj.foo();