궁금증/JavaScript

[모던 자바스크립트 deep dive] 21장 this

하나둘세현 2024. 2. 6. 01:45
728x90

this 키워드

동작을 나타내는 메서드는 자신이 속한 객체의 상태 -> 프로퍼티를 참조하고 변경 가능해야 한다.

메서드가 자신이 속한 객체의 프로퍼티를 참조하려면 자신이 속한 객체를 가리키는 식별자를 참조 가능해야한다.

 

const circle = {
  // 프로퍼티: 객체 고유의 상태 데이터
  radius: 5,
  // 메서드: 상태 데이터를 참조하고 조작하는 동작
  getDiameter() {
    // 이 메서드가 자신이 속한 객체의 프로퍼티나 다른 메서드를 참조하려면
    // 자신이 속한 객체인 circle을 참조 할 수 있어야 한다.
    return 2 * circle.radius;
  },
};

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

 

객체 리터럴은 circle 변수에 할당되기 직전에 평가된다. 

=> getDiameter 메서드가 호출되는 시점에 이미 객체 리터럴이 평가가 완려 되어 객체가 생성되고 circle 식별자에 생성된 객체가 할당된 이후이다. 

=> 메서드 내부에서 circle 식별자를 참조 가능

 

(저기 위에서의 객체 리터럴은 중괄호'{}'를 사용하여 정의된 것 전체! 즉, radius 프로퍼티와 getDiameter() 메서드를 의미)

function Ciurcle(radius) {
    // 이 시점에는 생성자 함수 자신이 생성할 인스턴스를 가리키는 식별자를 알 수 없다.
    ????.radius = radius;
}

circle.prototype.getDiameter = function() {
    // 이 시점에는 생성자 함수 자신이 생성할 인스턴스를 가리키는 식별자를 알 수 없다.
    return 2 * ????.radius;
};

// 생성자 함수로 인스턴스를 생성하려면 먼저 생성자 함수를 정의해야한다.
const circle = new circle(5);

 

생성자 함수로 인스턴스를 생성하려면 먼저 생성자 함수가 존재해야 한다.

 

자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 특수한 식별자가 필요하다. 

자바스크립트는 this를 통해 특별한 식별자를 제공

 

 

this

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

this를 통해 자신이 속한 객체 또는 자신이 생성할 인스턴스의 프로퍼티나 메서드를 참조 가능

  • 암묵적으로 생성, 코드 어디에서든지 참조 가능
  • 함수를 호출하면 arguements 객체와 this가 암묵적으로 함수 내부에 전달 
  • this가 가리키는 값, this 바인딩은 함수 호출 방식에 의한 동적으로 결정

(자바스크립트 내부에서 arguements 객체는 지역 변수처럼 사용 가능, this도 지역변수처럼 사용 가능)

this 바인딩
바인딩은 식별자와 값을 연결하는 과정을 의미
this 바인딩은 this(키워드로 분류되지만 식별자 역할)와 this가 가리킬 객체를 바인딩함

 

const circle = {
  radius: 5,
  getDiameter() {
    // this는 메서드를 호출한 객체를 가리킨다.
    return 2 * this.radius;
  },
};

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

 

this는 메서드를 호출한 객체 circle을 가리킨다.

// 생성자 함수
function Circle(radius) {
  // this는 생성자 함수가 생성할 인스턴스를 가리킨다.
  this.radius = radius;
}

Circle.prototype.getDiameter = function () {
  // this는 생성자 함수가 생성할 인스턴스를 가리킨다.
  return 2 * this.radius;
};

//인스턴스 생성
const circle = new Circle(5);
console.log(circle.getDiameter()); //10

 

 

생성자 함수 내부의 this -> 생성자 함수가 생성할 인스턴스

this는 상황에 따라 가리키는 대상이 다르다. 

 

  • this는 함수가 호출되는 방식에 따라 바인딩될 값, this 바인딩이 동적으로 결정
  • strict mode도 this 바인딩에 영향 (엄격 모드가 적용된 일반 함수 내부의 this에서는 undefined으로 바인딩)
  • this는 코드 어디서든 참조 가능(전역, 함수 내부에서 참조 가능)

 


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

this바인딩(this에 바인딩될 값) → 함수 호출 방식

함수가 어떻게 호출되었는지에 따라 동적으로 결정

 

함수 호출 방식

  1. 일반 함수 호출
  2. 메서드 호출
  3. 생성자 함수 호출
  4. Function.prototype.apply/call/bind 메서드에 의한 간접 호출
// this바인딩은 함수 호출 방식에 따라 동적으로 결정
const foo = funtcion() {
    console.dir(this);
};

// 동일한 함수도 다양한 방식으로 호출 가능

// 1. 일반 함수 호출
// foo 함수를 일반적인 방식으로 호출
// foo 함수 내부의 this는 메서드를 호출한 obj를 가리킨다.
foo(); // window

// 2. 메서드 호출
// foo 함수를 프로퍼티 값으로 할당하여 호출
// foo 함수 내부의 this는 메서드를 호출한 객체 obj를 가리ㅣ킨다.
const obj = {foo};
obj.foo(); //obj

// 3. 생성자 함수 호출
// foo 함수를 new 연산자와 함께 생성자 함수로 호출
// foo 함수 내부의 this는 생성자 함수가 생성한 인스턴스를 가리킨다.
new foo(); // foo {}

//4. Function.prototype.apply/call/bind 메서드에 의한 간접 호출
// foo 함수 내부의 this는 인수에 의해 결정
const bar = {name: 'bar'};

foo.call(bar); // bar
foo.apply(bar); //bar
foo.bind(bar)(); // bar

 

일반 함수 호출

기본적으로 this에는 전역 객체가 바인딩

function foo() {
  console.log("foo's this:", this); //window
  function bar() {
    console.log("bar's this: this"); // window
  }
  bar();
}
foo();

 

전역 함수는 물론 중첩 함수를 일반 함수로 호출하면 함수 내부의 this에는 전역 객체가 바인딩 된다.

function foo() {
  "use strict";

  console.log("foo's this: ", this); // undefined
  function bar() {
    console.log("bar's this: ", this); //undefined
  }
  bar();
}
foo();

 

strict mode가 적용된 일반 함수 내부의 this에는 undefined가 바인딩된다.

메서드 내에서 정의한 중첩 함수도 일반 함수로 호출되면 중첩 함수 내부의 this에는 전역 객체가 바인딩된다.

 

var value = 1;

const obj = {
  value: 100,
  foo() {
    console.log("foo's this: ", this); // {value: 100, foo: f}
    // 콜백 함수 내부의 this에는 전역 객체가 바인딩 된다.
    setTimeout(function () {
      console.log("callback's this: ", this); //window
      console.log("callback's this.value", this.value); //1
    }, 100);
  },
};

obj.foo();

 

setTimeout 함수
두 번째 인수로 전달한 시간(ms)만큼 대기한 다음, 첫 번째 인수로 전달한 콜백 함수로 호출하는 타이머 함수

 

일반 함수로 호출된 모든 함수(중첩 함수, 콜백 함수 포함) 내부의 this에는 전역 객체가 바인딩된다.

 

위의 예제에서는 메서드 내부에서 setTimeout 함수에 전달된 콜백 함수의 this에는 전역 객체가 바인딩되었다.

this.value는 전역 객체의 value의 프로퍼티로서 winow.value를 참고한다.

var 키워드로 선언한 전역 변수는 전역 객체의 프로퍼티가 되므로 winodw.value는 1이다.

 

var value = 1;

const obj = {
  value: 100,
  foo() {
    // 화살표 함수 내부의 this는 상위 스코프의 this를 가리킨다.
    setTimeout(() => console.log(this.value), 100); //100
  },
};

obj.foo();

 

메서드 호출

메서드 내부의 this에는 메서드를 호출한 객체, 즉 메서드를 호출할 때 메서드 이름 앞의 마침표(.) 연산자 앞에 기술한 객체가  바인딩된다.

⚠️ 메서드 내부의 this는 메서드를 소유한 객체가 아닌 메서드를 호출한 객체에 바인딩된다. 

const perosn = {
  name: "Lee",
  getName() {
    // 메서드 내부의 this는 메서드를 호출한 객체에 바인딩된다.
    return this.name;
  },
};

// 메서드 getName을 호출한 객체는 person이다.
console.log(person.getName()); //Lee

 

생성자 함수 호출

생성자 함수 내부의 this에는 (미래에) 생성할 인스턴스가 바인딩된다.

// 생성자 함수
function Circle(radius) {
  // 생성자 함수 내부의 this는 생성자 함수가 생성할 인스턴스를 가리킨다.
  this.radius = radius;
  this.getDiameter = function () {
    return 2 * this.radius;
  };
}

// 반지름이 5인 Circle 객체를 생성
const circle1 = new Circle(5);
// 반지름이 10인 Circle 객체를 생성
const circle2 = new Circle(10);

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

 

생성자 함수는 객체(인스턴스)를 생성하는 함수

// new 연산자와 함께 호출하지 않으면 생성자 함수로 동작하지 않는다. 즉, 일반적인 함수의 호출이다.
const circle3 = Circle(15);

// 일반 함수로 호출된 Circle에는 반환문이 없으므로 암묵적으로 undefined를 반환한다.
console.log(circle3); // undefined

// 일반 함수로 호출된 Circle 내부의 this는 전역 객체를 가리킨다.
console.log(radius); // 15

 

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

appy, call, bind 메서드는 Function.prototype의 메서드 → 모든 함수가 상속 받아 사용 가능

/**
 * 주어진 this 바인딩과 인수 리스트 배열을 사용하여 함수 호출
 * @param thisArg - this로 사용할 객체
 * @param argsArray - 함수에게 전달할 인수 리스트의 배열 또는 유사 배열 객체
 * @returns 호출한 함수의 반환값
 */
Function.prototype.apply(thisArg[, argsArray])

/**
 * 주어진 this바인딩과 ,로 구분된 인수 리스트를 사용하여 함수 호출
 * @param thisArg - this로 사용할 객체
 * @param arg1, arg2, ... -함수에게 전달할 인수 리스트
 * @return 호출된 함수의 반환값
 */

Function.prototype.call(thisArg[, age1[,arg2[, ...]]])

 

Function.prototype.apply, Function.prototype.call 메서드는 this로 사용할 객체와 인수 리스트를 인수로 전달받아 함수로 호출

function getThisBinding() {
  return this;
}

// this로 사용할 객체
const thisArg = { a: 1 };

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

// getThisBinding 함수를 호출하면서 인수로 전달한 객체를 getThisBinding 함수의 this에 바인딩한다.
console.log(getThisBinding.apply(thisArg)); // {a: 1}
console.log(getThisBinding.call(thisArg)); // {a: 1}

 

apply와 call 메서드는 함수를 호출하면서 번째 인수로 전달한 특정 객체를 호출한 함수의 this에 바인딩

함수 호출 방식 this 바인딩
일반 함수 호출 전역 객체
메서드 호출 메서드를 호출한 객체
생성자 함수 호출 생성자 함수가 (미래에)생성할 인스턴스
Function.prototype.apply/call/bind 메서드에 의한 간접 호출 Function.prototype.apply/call/bind 메서드에 첫 번째 인수로 전달된 객체
728x90