이야기 정리

자바스크립트의 This란? 본문

개발공부/JavaScrit

자바스크립트의 This란?

jinhistory 2023. 3. 17. 12:02

자바스크립트를 공부하다보면 꼭 나오는 것 중 하나가 바로 this다. this는 호출에 따라 의미가 달라져 헛갈리기 때문에 간단한 예시들과 함께 this가 필요한 이유부터 살펴보려 한다.

 

this는 왜 필요할까?

먼저 생성자 함수를 생각해보자. 생성자 함수를 만드는 과정은 다음과 같다.

  1. 생성자 함수를 정의한다.
  2. 프로퍼티나 메서드를 추가한다.
  3. new를 사용해 인스턴스를 생성한다.

문제는 2번에서 프로퍼티나 메서드를 만들 때 3번에 있는 인스턴스의 값이 필요하다. 하지만 정의한 시점에서는 인스턴스를 가리키는 식별자를 알 수 없다. 이 때문에 this가 필요하다.

 

// 1,2번 과정 : 여기서는 ??? 안에 뭐가 들어올지 알 수가 없다.
function Obj(x, y) {
    ???.x = x
    ???.y = y

    ???.add = function () {
        return x+y
    }
}

// 3번과정 : 이때 인스턴스를 생성한다.
const obj = new Obj(5, 5)
console.log(obj.add())

 

그렇다면 this란 무엇일까?
  • 함수 내에서 사용되는 변수(자기 참조 변수)이며, 함수 호출 방식에 따라 의미가 달라진다. (자바스크립트만의 특징!)
  • 즉, this는 함수가 소속되어 있는 객체를 가리킨다!!
  • this가 가리키는 값을 결정 짓는 것을 this 바인딩이라고 한다.

 

바인딩이란?

식별자와 값을 연결하는 과정

const a(식별자) = 1(값)

예를 들어, 위처럼 변수를 선언했을 때 코드는 a(식별자)와 1(값)을 바인딩해주는 것이다.

 

 

 

함수 호출방식과 this 바인딩


1. 일반 함수 호출

  • 전역 객체(window)가 호출된다.
  • 일반 호출이면 중첩 함수, 콜백함수도 전역 객체가 호출된다.
function foo() {
    console.log(this) //window

    function bar() {
        console.log(this) //window
    }
    bar()
}
foo()

 

2. 메소드 호출

  • 객체 안에 프로포티를 함수로 하면 메소드가 된다.
  • 메소드가 소속된 객체(. 앞에 있는 객체)가 바로 this다.
예제1 : 다른 객체에서 호출하기
const obj = {
    name : 'kim',
    getName : function () {
        console.log(this) // {name: 'kim', getName: ƒ}
        console.log(this.name) //kim
    }
}

const obj2 = {
    name : 'park',
    getName : obj.getName //{name: 'park', getName: ƒ}, park
}

// obj2에서 호출했기 때문에 park이 나타나게 된다.
obj2.getName()

 

주의하기!!! 

메서드를 일반 함수로 호출하면 마찬가지로 전역객체가 호출된다.
const obj = {
    name : 'kim',
    getName : function () {
        console.log(this) // {name: 'kim', getName: ƒ}
        console.log(this.name) //kim
    }
}

// 일반 호출이기 때문에 window로 나타난다.
const name = obj.getName
name() // window

 

예제 2 : 중첩함수

중첩함수를 일반함수로 호출하면 마찬가지로 전역객체가 호출된다. 아래 예제를 해결하는 방법은 2가지가 있다.

const obj = {
    name : 'kim',
    age: 20,
    getName : function () {
        console.log(this) // {name: 'kim', age: 20, getName: ƒ}
        console.log(this.name) //kim

        // 중첩함수는 일반호출을 했기 때문에 전역 객체가 호출된다.
        function getAge() {
            console.log(this) // window
            console.log(this.age) // undifned
        }
        getAge()
    }
}

obj.getName()

 

해결법 : 화살표 함수나 변수 사용하기
const obj = {
    name : 'kim',
    age: 20,
    getName : function () {
        console.log(this) // {name: 'kim', age: 20, getName: ƒ}
        console.log(this.name) //kim

        // 방법 1 : 화살표함수 사용
        const getAge = () => {
            console.log(this) // {name: 'kim', age: 20, getName: ƒ}
            console.log(this.age) // 20
        }
        getAge()

        // 방법 2 : 변수 사용
        const then = this
        function getAge2() {
            console.log(then) // {name: 'kim', age: 20, getName: ƒ}
            console.log(then.age) // 20
        }
        getAge2()
    }
}

obj.getName()

 

 

3. 생성자 함수

  • new와 함께 생성된다.
  • 이때, 새로 생성되는 인스턴스(객체)가 this가 된다.
function Obj(name) {
    this.name = name
    this.getName = function () {
        return this.name
    } 
}

const obj = new Obj('kim')
console.log(obj.getName()) // kim

 

 

4. 객체로서의 함수 : .apply, .call, .bind

함수는 객체이기 때문에 메서드를 사용할 수 있다.

this의 값을 고정시키기 위한 메서드는 총 3종류로 apply와 call은 함수를 호출하지만, bind는 호출하지 않는다는 차이점이 있다. 먼저 함수를 호출하는 apply와 call에 대해 알아보자.

 

.apply, .call
  • 첫인자가 this가 되며, 객체를 this로 전달한다.
  • 인수를 전달하는 방식만 다를 뿐, this로 사용할 객체를 전달하면서 함수를 호출한다는 점은 동일하다.
//사용법
함수이름.apply({객체이름 : 객체값}, [인수 리스트]);
함수이름.call({객체이름 : 객체값}, 인수1, 인수2, 인수3);

주로 arguments 등의 유사 배열 객체에 배열 메서드를 사용하는 경우에 이용한다.

function obj() {
    console.log(this) // {name: 'kim'}
    console.log('Hi, ' + this.name) // Hi, kim
}

console.log(obj.apply({name: 'kim'}))

function obj2(arr) {
    console.log(arguments) //[1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
    const a = Array.prototype.slice.call(arguments) // [1, 2, 3]
    const b = a.map(a => this.number + a)
    return b
}

console.log(obj2.apply({number: 10}, [1,2,3])) // [11, 12, 13]

 

.bind
  • apply, call과 달리 함수를 실행시키지 않는다. 때문에 별도로 실행시켜줘야한다.
function obj() {
    console.log(this) // {name: 'kim'}
    console.log('Hi, ' + this.name) // Hi, kim
}
// 함수를 실행시키지 않는다.
console.log(obj.bind({name: 'kim'})) // ƒ obj()
// 별도로 함수를 실행시켜야한다.
console.log(obj.bind({name: 'kim'})()) // {name: 'kim'}, Hi, kim

 

예제 : 다음처럼 함수의 일반 호출이나, 중첩함수에서 사용할 수 있다.
function getName () {
    console.log(this) // {name: 'kim', age: 20, getName: ƒ}
    console.log(this.name) //kim

    const binding2 = getAge.bind(this)
    function getAge() {
        console.log(this) // {name: 'kim', age: 20}
        console.log(this.age) // 20
    }
    binding2()
}
const binding = getName.bind({name: 'kim', age: 20})
binding()

주의하기!!! 

화살표 함수를 사용하면 함수가 속해있는 곳의 상의 this를 계승한다. bind를 사용할 수 없다.

 

 

 

참조

모던자바스크립트 Deep Dive - 이웅모 342p~358p

https://www.youtube.com/watch?v=4ACSJlzJjJs&feature=youtu.be 

https://www.youtube.com/watch?v=tDZROpAdJ9w

Comments