분류 javascript

JavaScript에서 This, Bind, Call 및 Apply 이해 - JavaScript guide

컨텐츠 정보

  • 조회 450 (작성일 )

본문

목차​

  1. JavaScript 개발자 콘솔을 사용하는 방법
  2. HTML에 JavaScript를 추가하는 방법
  3. JavaScript에서 구문 및 코드 구조 이해
  4. JavaScript로 주석을 작성하는 방법
  5. JavaScript에서 데이터 유형 이해
  6. JavaScript에서 문자열로 작업하는 방법
  7. JavaScript에서 문자열을 인덱싱, 분할 및 조작하는 방법
  8. JavaScript에서 데이터 유형을 변환하는 방법
  9. JavaScript에서 변수, 범위 및 게양 이해
  10. 연산자를 사용하여 JavaScript에서 수학을 수행하는 방법
  11. JavaScript에서 배열 이해
  12. JavaScript에서 배열 메서드를 사용하는 방법 : Mutator 메서드
  13. JavaScript에서 배열 메서드를 사용하는 방법 : Accessor 메서드
  14. JavaScript에서 배열 메서드를 사용하는 방법 : Iteration 메서드
  15. JavaScript에서 객체 이해
  16. JavaScript에서 날짜 및 시간 이해
  17. JavaScript에서 이벤트 이해
  18. JavaScript에서 JSON으로 작업하는 방법
  19. JavaScript로 조건문을 작성하는 방법
  20. JavaScript에서 Switch 문을 사용하는 방법
  21. JavaScript에서 While 및 Do ... While 루프 사용
  22. JavaScript에서 For 루프를 생성하는 방법
  23. JavaScript에서 함수를 정의하는 방법
  24. JavaScript의 프로토 타입 및 상속 이해
  25. JavaScript에서 클래스 이해
  26. JavaScript에서 객체 메소드를 사용하는 방법
  27. JavaScript에서 This, Bind, Call 및 Apply 이해

JavaScript에서 This, Bind, Call 및 Apply 이해 


소개


this 키워드는 JavaScript에서 매우 중요한 개념이며 특히 새로운 개발자와 다른 프로그래밍 언어에 경험이 있는 사람 모두에게 혼동되는 키워드입니다. JavaScript에서 this는 객체에 대한 참조입니다. this가 참조하는 오브젝트는 글로벌인지, 오브젝트인지 또는 생성자인지에 따라 내재적으로 다양 할 수 있으며 함수 Function prototype 메소드 bind, call 및 apply의 사용법에 따라 명시적으로 달라질 수도 있습니다.


this 주제는 다소 복잡한 주제이지만 첫 번째 JavaScript 프로그램을 작성하자마자 나타나는 주제이기도 합니다. DOM (Document Object Model)에서 요소 또는 이벤트에 액세스하거나 객체 지향 프로그래밍 스타일로 작성하기 위한 클래스를 작성하거나 일반 객체의 속성 및 메서드를 사용하려고 할 때 this 문제가 발생합니다.


이 기사에서는 컨텍스트를 기반으로 암시적으로 this가 무엇을 의미하는지 배우고, bind, call 및 apply 메소드를 사용하여 this 값을 명시적으로 결정하는 방법에 대해 학습합니다.


https://www.digitalocean.com/community/conceptual_articles/understanding-this-bind-call-and-apply-in-javascript?status=moved_permanently 


암시적 컨텍스트 


this 값이 암시적으로 유추 될 수 있는 네 가지 주요 컨텍스트가 있습니다.


  • the global context
  • as a method within an object
  • as a constructor on a function or class
  • as a DOM event handler

Global 


글로벌 컨텍스트에서 this는 글로벌 오브젝트를 나타냅니다. 브라우저에서 작업 할 때 전역 컨텍스트는 창입니다. Node.js에서 작업 할 때 글로벌 컨텍스트는 글로벌입니다.


참고 : JavaScript의 범위 개념에 아직 익숙하지 않은 경우 JavaScript에서 변수, 범위 및 호이스트 이해를 검토하십시오. 


예를 들어 브라우저의 개발자 도구 콘솔에서 코드를 연습합니다. 브라우저에서 JavaScript 코드를 실행하는 데 익숙하지 않은 경우 JavaScript 개발자 콘솔 사용 방법을 읽으십시오.


다른 코드없이 this 값을 기록하면 this가 어떤 객체를 참조하는지 볼 수 있습니다.


console.log(this)
Output
Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}

this가 브라우저의 전역 객체인 창(window)임을 알 수 있습니다.


JavaScript에서 변수, 범위 및 호이스트 이해에서 함수에는 변수에 대한 고유 한 컨텍스트가 있음을 배웠습니다. this가 함수 내에서 동일한 규칙을 따를 것이라고 생각할 수도 있지만 그렇지 않습니다. 최상위 함수는 여전히 전역 객체에 대한 this 참조를 유지합니다.


다음과 같이 최상위 함수 또는 개체와 관련이 없는 함수를 작성합니다.


function printThis() {
  console.log(this)
}

printThis()
Output
Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}

함수 내에서도 여전히 창 또는 전역 객체를 나타냅니다.


그러나 엄격 모드를 사용하는 경우 전역 컨텍스트의 함수 내에서 컨텍스트가 정의되지 않습니다.

'use strict'

function printThis() {
  console.log(this)
}

printThis()
Output
undefined

일반적으로 엄격 모드를 사용하여 예상치 못한 범위를 가질 가능성을 줄이는 것이 더 안전합니다. 누군가 this를 사용하여 창 객체를 참조하는 경우는 거의 없습니다.


엄격 모드 및 실수 및 보안 관련 변경 사항에 대한 자세한 내용은 MDN의 엄격 모드 설명서를 참조하십시오. 


객체 메소드 


메소드는 객체의 함수 또는 객체가 수행 할 수 있는 작업입니다. 메소드는 this를 사용하여 객체의 속성을 참조합니다.


const america = {
  name: 'The United States of America',
  yearFounded: 1776,

  describe() {
    console.log(`${this.name} was founded in ${this.yearFounded}.`)
  },
}

america.describe()
Output
"The United States of America was founded in 1776."

이 예에서 this는 america와 동일합니다.


중첩 된 객체에서 this는 메소드의 현재 객체 범위를 나타냅니다. 다음 예에서 details 객체 내의 this.symbol은 details.symbol을 나타냅니다.


const america = {
  name: 'The United States of America',
  yearFounded: 1776,
  details: {
    symbol: 'eagle',
    currency: 'USD',
    printDetails() {
      console.log(`The symbol is the ${this.symbol} and the currency is ${this.currency}.`)
    },
  },
}

america.details.printDetails()
Output
"The symbol is the eagle and the currency is USD."

그것에 대해 생각하는 또 다른 방법은 this가 메소드를 호출 할 때 점의 왼쪽에 있는 객체를 참조한다는 것입니다.


함수 생성자 


new 키워드를 사용하면 생성자 함수 또는 클래스의 인스턴스가 생성됩니다. ECMAScript 2015 업데이트에서 클래스 구문이 JavaScript에 도입되기 전에 함수 생성자가 사용자 정의 객체를 초기화하는 표준 방법이었습니다. JavaScript의 클래스 이해에서 함수 생성자와 동등한 클래스 생성자를 작성하는 방법을 학습합니다.


function Country(name, yearFounded) {
  this.name = name
  this.yearFounded = yearFounded

  this.describe = function() {
    console.log(`${this.name} was founded in ${this.yearFounded}.`)
  }
}

const america = new Country('The United States of America', 1776)

america.describe()
Output
"The United States of America was founded in 1776."

이와 관련하여 this는 america 상수에 포함 된 Country 인스턴스에 바인딩됩니다.


클래스 생성자 


클래스의 생성자는 함수의 생성자와 동일하게 작동합니다. JavaScript의 클래스 이해에서 함수 생성자와 ES6 클래스의 유사점과 차이점에 대해 자세히 알아보십시오.

class Country {
  constructor(name, yearFounded) {
    this.name = name
    this.yearFounded = yearFounded
  }

  describe() {
    console.log(`${this.name} was founded in ${this.yearFounded}.`)
  }
}

const america = new Country('The United States of America', 1776)

america.describe()

설명 방법에서 this는 america Country의 인스턴스를 나타냅니다.

Output
"The United States of America was founded in 1776."

DOM 이벤트 핸들러 


브라우저에는 이벤트 핸들러를 위한 특수한 컨텍스트가 있습니다. addEventListener에 의해 호출 된 이벤트 핸들러에서 this는 event.currentTarget을 참조합니다. 종종 개발자는 DOM의 요소에 액세스하는 데 필요한 대로 event.target 또는 event.currentTarget을 사용하지만 this 참조에서 this 참조가 변경되므로 알고 있어야 합니다.


다음 예에서는 버튼을 만들고 텍스트를 추가 한 다음 DOM에 추가합니다. 이벤트 핸들러 내에서 this 값을 기록하면 대상이 인쇄됩니다.


const button = document.createElement('button')
button.textContent = 'Click me'
document.body.append(button)

button.addEventListener('click', function(event) {
  console.log(this)
})
Output
<button>Click me</button>

this를 브라우저에 붙여 넣으면 페이지에 “클릭하십시오” 라는 단추가 추가됩니다. 버튼을 클릭하면 버튼 자체가 요소를 기록하므로 콘솔에 <button> 클릭하십시오. </ button>가 나타납니다. 따라서 보시다시피, this는 이벤트 리스너를 추가 한 요소 인 대상 요소를 나타냅니다.


명시적 컨텍스트 


이전의 모든 예제에서 this 값은 컨텍스트, 객체, 생성 된 함수 또는 클래스 또는 DOM 이벤트 핸들러 중 어디에서 컨텍스트에 의해 결정되었습니다. 그러나 call, apply 또는 bind를 사용하여 this가 참조 할 내용을 명시적으로 결정할 수 있습니다.


프로그램의 컨텍스트에 따라 호출, 적용 또는 바인딩을 사용할 시기를 정확하게 정의하기가 어렵습니다. bind는 이벤트를 사용하여 다른 클래스 내 한 클래스의 속성에 액세스하려는 경우 특히 유용합니다. 예를 들어 간단한 게임을 작성하는 경우 사용자 인터페이스와 I / O를 한 클래스로 분리하고 게임 논리와 상태를 다른 클래스로 분리 할 수 ​​있습니다. 게임 로직은 키 누르기 및 클릭과 같은 입력에 액세스 해야 하므로 이벤트를 바인딩하여 게임 로직 클래스의이 값에 액세스 하려고 합니다.


중요한 부분은 this가 의미하는 대상을 결정하는 방법을 아는 것입니다. 이전 섹션에서 배운 것을 내재적으로 또는 다음에 배울 세 가지 방법으로 명시적으로 할 수 있습니다.


Call and Apply 


call 및 apply는 매우 유사합니다. 지정된 컨텍스트와 선택적 인수를 사용하여 함수를 호출합니다. call과 apply의 유일한 차이점은 call은 인수를 하나씩 전달해야 하며 apply는 인수를 배열로 취한다는 것입니다.


이 예제에서는 객체를 만들고 this를 참조하지만 this 컨텍스트가 없는 함수를 만듭니다.

const book = {
  title: 'Brave New World',
  author: 'Aldous Huxley',
}

function summary() {
  console.log(`${this.title} was written by ${this.author}.`)
}

summary()
Output
"undefined was written by undefined"

요약과 책은 서로 연결되어 있지 않으므로 요약을 호출하면 전역 객체에서 해당 속성을 찾기 때문에 정의되지 않은 인쇄 만 인쇄합니다.


참고 : 엄격 모드에서 this를 시도하면 Uncaught TypeError : undefined의 'title'속성을 읽을 수 없습니다. 


그러나 call 및 apply를 사용하여 함수에 대한 this 책 컨텍스트를 호출 할 수 있습니다.

summary.call(book)
// or:
summary.apply(book)
Output
"Brave New World was written by Aldous Huxley."

이러한 방법을 적용 할 때 책과 요약이 연결됩니다. this가 무엇인지 정확히 확인합시다.

function printThis() {
  console.log(this)
}

printThis.call(book)
// or:
whatIsThis.apply(book)
Output
{title: "Brave New World", author: "Aldous Huxley"}

이 경우, this는 실제로 인수로 전달 된 객체가 됩니다.


이것이 call과 apply가 같은 방식이지만 작은 차이가 있습니다. this 컨텍스트를 첫 번째 인수로 전달할 수 있을 뿐만 아니라 추가 인수를 전달할 수도 있습니다.

function longerSummary(genre, year) {
  console.log(
    `${this.title} was written by ${this.author}. It is a ${genre} novel written in ${year}.`
  )
}

호출하면 전달하려는 각 추가 값이 추가 인수로 전송됩니다.


longerSummary.call(book, 'dystopian', 1932)
Output
"Brave New World was written by Aldous Huxley. It is a dystopian novel written in 1932."

apply와 똑같은 인수를 보내려고 하면 다음과 같이 됩니다.

longerSummary.apply(book, 'dystopian', 1932)
Output
Uncaught TypeError: CreateListFromArrayLike called on non-object at <anonymous>:1:15

대신 적용하려면 모든 인수를 배열로 전달해야 합니다.

longerSummary.apply(book, ['dystopian', 1932])
Output
"Brave New World was written by Aldous Huxley. It is a dystopian novel written in 1932."

인수를 개별적으로 또는 배열로 전달하는 것의 차이는 미묘하지만 알고 있어야 합니다. 일부 매개 변수 세부 사항이 변경된 경우 함수 호출을 변경할 필요가 없으므로 apply를 사용하는 것이 더 간단하고 편리 할 수 ​​있습니다.


Bind 


call과 apply는 일회성 사용 메소드입니다. this 컨텍스트를 사용하여 메소드를 호출하면 해당 메소드를 가지지만 원래 함수는 변경되지 않습니다.


때로는 다른 객체의 this 컨텍스트와 함께 메소드를 계속 사용해야 할 수도 있으며,이 경우 bind 메소드를 사용하여 명시 적으로 this를 바인딩 된 새 함수를 작성할 수 있습니다.

const braveNewWorldSummary = summary.bind(book)

braveNewWorldSummary()
Output
"Brave New World was written by Aldous Huxley"

이 예제에서는 braveNewWorldSummary를 호출 할 때마다 항상 this 값이 바인딩 된 원래 값을 반환합니다. this 컨텍스트에 새 컨텍스트를 바인딩 하려고 시도하면 실패하므로 항상 바인딩 된 함수를 신뢰하여 원하는 값을 반환 할 수 있습니다.

const braveNewWorldSummary = summary.bind(book)

braveNewWorldSummary() // Brave New World was written by Aldous Huxley.

const book2 = {
  title: '1984',
  author: 'George Orwell',
}

braveNewWorldSummary.bind(book2)

braveNewWorldSummary() // Brave New World was written by Aldous Huxley.

이 예제는 braveNewWorldSummary를 다시 한 번 바인 하려고 시도하지만 처음 바인딩 된 이후부터 this 컨텍스트를 유지합니다.


화살표 함수 


화살표 함수에는 this 바인딩이 없습니다. 대신, 그들은 다음 단계의 실행으로 올라갑니다.

const whoAmI = {
  name: 'Leslie Knope',
  regularFunction: function() {
    console.log(this.name)
  },
  arrowFunction: () => {
    console.log(this.name)
  },
}

whoAmI.regularFunction() // "Leslie Knope"
whoAmI.arrowFunction() // undefined

실제로 외부 컨텍스트를 참조하려는 경우 화살표 함수를 사용하는 것이 유용 할 수 있습니다. 예를 들어, 클래스 내부에 이벤트 리스너가 있는 경우 클래스의 일부 값을 참조하려고 할 수 있습니다.


이 예제에서는 이전처럼 DOM에 버튼을 만들어 추가하지만 클래스에는 클릭시 버튼의 텍스트 값을 변경하는 이벤트 리스너가 있습니다.


const button = document.createElement('button')
button.textContent = 'Click me'
document.body.append(button)

class Display {
  constructor() {
    this.buttonText = 'New text'

    button.addEventListener('click', event => {
      event.target.textContent = this.buttonText
    })
  }
}

new Display()

버튼을 클릭하면 텍스트 내용이 buttonText 값으로 변경됩니다. 여기서 화살표 함수를 사용하지 않은 경우 이는 event.currentTarget과 같으며 명시적으로 바인딩 하지 않고 클래스 내 값에 액세스하는 데 사용할 수 없습니다. 이 전략은 종종 React와 같은 프레임 워크의 클래스 메소드에서 사용됩니다.


결론 


이 기사에서는 JavaScript에서 this에 대해 배웠으며, 암시적 런타임 바인딩 및 바인드, 호출 및 적용을 통한 명시적 바인딩을 기반으로 할 수 있는 다양한 값에 대해 배웠다. 또한 화살표 함수에서 this 바인딩이 부족하여 다른 컨텍스트를 참조하는 방법에 대해서도 배웠습니다. 이 지식을 통해 프로그램에서 this 값을 결정할 수 있어야 합니다.