본문 바로가기
📖 책 찢기/코어 자바스크립트

[코어자바스크립트] 3장. this

by 짱돌보리 2024. 6. 14.
728x90

[3장] this

❓this

대부분의 객체지향언어에서 - 클래스로 생성한 인스턴스 객체

JS에서 - 어디서든 사용 가능 (상황에 따라 this가 바라보는 대상이 달라짐)

→ 함수와 객체(메서드)의 구분이 느슨한 자바스크립트에서 this는 실질적으로 이 둘을 구분하는 거의 유일한 기능임!

1. 상황에 따라 달라지는 this

  • this는 기본적으로 실행 컨텍스트가 생성될 때 함께 결정된다.
  • 실행 컨텍스트는 함수를 호출할 때 생성함 = this는 함수를 호출할 때 결정된다.

📍전역 공간에서의 this

  • 전역공간의 this = 전역객체
  • 브라우저에서의 전역객체 = window
  • Node.js에서의 전역객체 = global
var a = 1
console.log(a) // 1
console.log(window.a) // 1 (브라우저 환경에서)
console.log(this.a) // 1 (전역 스코프에서, 브라우저 환경에서는 window.a와 동일)

→ 전역변수를 선언하면 자바스크립트 엔진은 이를 전역객체의 프로퍼티로 할당한다.

var a = 1
delete window.a // false

var b = 2
delete b // false

window.c = 3
delete window.c // true

window.d = 4
delete d // true
  • 처음부터 전역객체의 프로퍼티로 할당한 경우 - 삭제 O
  • 전역변수로 선언한 경우 - 삭제X
  • 사용자가 의도치 않게 삭제하는 것을 방지하는 차원..

📍함수, 메서드로서 호출할 때 그 함수, 메서드 내부에서의 this

👊🏻함수 vs. 메서드

  • 함수 - 독립성 O
  • 메서드 - 독립성X
var a = 10
// 함수 정의
function greet() {
  console.log('이 함수의 this는 전역 객체를 가리킵니다.')
  console.log('전역 객체의 속성에 접근할 수 있습니다.')
  console.log('this.a:', this.a) // this.a: 10
}

// 메서드 정의
var obj = {
  greet: function () {
    console.log('이 메서드의 this는 객체를 가리킵니다.')
    console.log('객체의 속성에 접근할 수 있습니다.')
    console.log('this.a:', this.a) // this.a: undefined
  },
}

// 함수 호출
greet()

// 메서드 호출
obj.greet()

🤔메서드 내부 함수에서의 this를 우회하는 방법

외부 변수에 this를 할당한 다음, 내부 함수에서 그 변수를 참조하는 것

var obj = {
  name: '보리',
  greet: function () {
    var self = this // 외부 변수에 this 할당

    function innerFunction() {
      console.log('안녕하세요, ' + self.name + '님!')
    }

    innerFunction()
  },
}

obj.greet() // 안녕하세요, 보리님!

🤔this를 바인딩하지 않는 함수

  • 함수가 호출될 때 this 컨텍스트가 바인딩되지 않도록 하려면 일반적으로 화살표 함수(arrow function)를 사용한다.
  • 화살표 함수는 함수가 정의된 시점에서 상위 스코프의 this를 캡처하기 때문에 함수가 호출될 때 this가 변경되지 않는다.
var obj = {
  name: '보리',
  greet: function () {
    setTimeout(() => {
      console.log('안녕하세요, ' + this.name + '님!')
    }, 1000)
  },
}

obj.greet() // (1초 후) "안녕하세요, 보리님!
  • 콜백 함수 내부의 thisobj 객체를 가리킨다.
  • 만약 화살표 함수 대신 일반 함수를 사용했다면, setTimeout 내에서 thiswindow 객체를 가리키게 될 것임…

📍생성자 함수 내부에서의 this

  • 생성자 함수 내부에서 this는 새로 생성된 객체를 가리킨다.
  • 생성자 함수는 new 키워드를 사용하여 호출되며, 이때 this는 새로 생성된 객체에 바인딩된다.
function Person(name, age) {
  // 새로운 객체에 속성 할당
  this.name = name
  this.age = age

  // 메서드 정의
  this.greet = function () {
    console.log(
      '안녕하세요, 제 이름은 ' +
        this.name +
        '이고, 나이는 ' +
        this.age +
        '살입니다.'
    )
  }
}

// 생성자 함수를 사용하여 객체 생성
var person1 = new Person('보리', 30)
var person2 = new Person('보라', 25)

// 객체의 메서드 호출
person1.greet() // "안녕하세요, 제 이름은 보리이고, 나이는 30살입니다."
person2.greet() // "안녕하세요, 제 이름은 보라이고, 나이는 25살입니다."

2. 명시적으로 this를 바인딩하는 방법

📍call

  • 메서드의 호출 주체인 함수를 즉시 실행하도록 하는 명령
  • 첫 번째 인자를 this로 바인딩하고, 이후의 인자들을 호출할 함수의 매개변수로 한다.
var person = {
  name: '보리',
  greet: function () {
    console.log('안녕하세요, ' + this.name + '님!')
  },
}

var anotherPerson = {
  name: '모리',
}

person.greet() // "안녕하세요, 보리님!"
person.greet.call(anotherPerson) // "안녕하세요, 모리님!"

call 메서드를 사용하여 person.greet 함수를 호출할 때 thisanotherPerson 객체로 설정

📍apply

  • call 메서드와 기능적을 완전히 동일
  • 두 번째 인자를 배열로 받아 그 배열의 요소들을 호출할 함수의 매개변수로 지정함
var person = {
  name: '보리',
  greet: function (greeting) {
    console.log(greeting + ', ' + this.name + '님!')
  },
}

var anotherPerson = {
  name: '모리',
}

person.greet('안녕하세요') // "안녕하세요, 보리님!"
person.greet.apply(anotherPerson, ['안녕하세요']) // "안녕하세요, 모리님!"

💡유사배열객체에 배열 메서드 적용

var obj = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3,
}

// obj에 push 메서드 호출하여 요소 추가
Array.prototype.push.call(obj, 'd')
console.log(obj) // {0: "a", 1: "b", 2: "c", 3: "d", length: 4}

// obj를 배열로 변환하여 arr에 저장
var arr = Array.prototype.slice.call(obj)
console.log(arr) // ["a", "b", "c", "d"]

💡Array.from()

  • 유사 배열 객체나 이터러블 객체를 배열로 변환하는 메서드
  • 제공된 객체나 값으로 새로운 Array 객체를 만든다.
var obj = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3,
}

var arr = Array.from(obj)
console.log(arr) // ["a", "b", "c"]

💡생성자 내부에서 다른 생성자 호출

function Animal(type) {
  this.type = type
}

function Person(name, age, type) {
  // Person 생성자 함수 내에서 Animal 생성자 함수 호출
  // Animal 생성자 함수가 현재 Person 생성자 함수의 인스턴스인 this 컨텍스트로 호출
  // Person 인스턴스에 type 속성이 추가되어 초기화
  Animal.call(this, type)
  this.name = name
  this.age = age
}

var bori = new Person('bori', 30, 'Dog')
console.log(bori) // Person { type: 'Dog', name: 'bori', age: 30 }

📍bind

  • call과 비슷하지만 즉시 호출하지는 않고 넘겨 받은 this 및 인수들을 바탕으로 새로운 함수를 반환하기만 하는 메서드
  • 새로운 함수를 호출할 때 인수를 넘기면 그 인수들은 기존 bind 메서드를 호출할 때 전달했던 인수들의 뒤에 이어서 등록된다.
var person = {
  name: '보리',
  greet: function (greeting) {
    console.log(greeting + ', ' + this.name + '님!')
  },
}

var anotherPerson = {
  name: '모리',
}

var greetAnotherPerson = person.greet.bind(anotherPerson, '안녕하세요')

// greetAnotherPerson 함수를 호출할 때 "안녕하세요"라는 인수가 이미 고정
greetAnotherPerson() // "안녕하세요, 모리님!"

💡상위 컨텍스트의 this를 내부함수나 콜백 함수에 전달하기

function outerFunction() {
  // 상위 컨텍스트의 this
  console.log(this) // window { ... }
  function innerFunction() {
    // bind 메서드로 전달된 this
    console.log(this) // window { ... }
  }
  innerFunction.bind(this)()
}

outerFunction()

📍화살표 함수의 예외사항

function outerFunction() {
  // // 상위 컨텍스트의 this
  console.log(this) // window { ... }
  var innerFunction = () => {
    // 상위 컨텍스트의 this를 캡처
    console.log(this) // window { ... }
  }
  innerFunction()
}

outerFunction()
  • 화살표 함수 내부에는 this가 아예 없으며 접근하고자 하면 스코프체인상 가장 가까운 this에 접근하게 된다.

📍별도의 인자로 this를 받는 경우 (콜백 함수 내에서의 this)

  • 콜백 함수를 인자로 받는 메서드 중 일부는 추가로 this로 지정할 객체를 인자로 지정할 수 있는 경우가 있다.
  • 내부 요소에 대해 같은 동작을 반복 수행해야 하는 배열 메서드에 많이 포진돼있음.
  • set, map, forEach 등…
var obj = {
  name: '보리',
  print: function () {
    var arr = [1, 2, 3]
    arr.forEach(function (element) {
      console.log(this.name + '의 번호:', element)
    }, this) // 두 번째 인자로 this를 전달
  },
}

obj.print()

// 출력 결과
// 보리의 번호: 1
// 보리의 번호: 2
// 보리의 번호: 3

✨정리

암묵적 this 바인딩

  • 전역 공간에서의 this는 전역 객체를 참조한다. (브라우저 - window, Node.js - global)
  • 어떤 함수를 메서드로서 호출한 경우 this는 호출 주체(메서드명 앞의 객체)를 참조한다.
  • 어떤 함수를 함수로서 호출한 경우 this는 전역 객체를 참조한다.
  • 콜백 함수 내부에서의 this는 해당 콜백 함수의 제어권을 넘겨받은 함수가 정의한 바에 따르며, 정의하지 않은 경우에는 전역 객체 참조한다.
  • 생성자 함수에서의 this는 생성될 인스턴스를 참조합니다.

명시적 this 바인딩

  • call, apply 메서드는 this를 명시적으로 지정하면서 함수 또는 메서드를 호출한다.
  • bind 메서드는 this 및 함수에 넘길 인수를 일부 지정해서 새로운 함수를 만든다.
  • 요소를 순회하면서 콜백 함수를 반복 호출하는 내용의 일부 메서드는 별도의 인자로 this를 받기도 한다.