• [JS] 자바스크립트 this

    2023. 3. 15.

    by. 지은이: 김지은

    728x90

     

    this?

    보통 자바스크립트에서 가장 사용하기 어렵거나 이해하기 어려운 걸 뽑자고 하면 this라고 할 것이다.

     

    this를 말로 설명하면 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수.

    함수를 호출할 때 결정되어 어떤 방식으로 호출하느냐에 따라 값이 달라진다.

     

    이해가 안된다면 아래 예시를 보자!

     

    먼저 this라는 걸 그냥 전역 공간에 콘솔을 찍어보면 window 객체를 가리킨다.

    이 window는 그냥 현재 열려있는 브라우저 창이라고 생각하면 된다.

     

    그렇다면 함수를 호출할 때 this는 어떤값을 반환할까?

     

    함수 호출

     

    myFunc() 함수는 전역 공간에서 호출되고 있다. 

    맨위에서 말했듯이 함수를 호출할 때 호출 방식에 따라 this 값이 결정이 되기 때문에 this는 전역객체인 window 객체를 가리키게 된다.

     

    또한, 위 함수 안에 다른 함수를 선언 후 호출해도 this는 window 객체를 가리키게 된다.

    그 내부함수는 myFunc() 안에서 호출된것이기 때문에 내부 함수도 전역 객체인 window를 가리키게 되는 것

     

    그렇다면 this는 window 객체라고 알고있으면 될까? 그건 아니다.

     

    메서드 호출

    let obj1 = {
        name: 'john',
        hello: function() {
            console.log('hello! ' + this.name);
        }
    }
    
    let obj2 = {
        name: 'amy',
        hello: obj1.hello
    }
    
    obj1.hello();
    obj2.hello();

    위 처럼 두 객체를 생성하고 각 객체는 'name'이라는 속성과 'hello' 메서드가 들어있다.

     

    1. obj1 객체는 name 속성이 'john' 이며, hello 메서드는 함수를 값으로 갖는데 이 함수는 hello! 와 this.name을 출력한다.

    2. obj2 객체는 name 속성이 'amy' 이고, hello 메서드는 obj1.hello 즉, obj1의 hello 메서드를 obj2의 hello 메서드로 할당한 것

     

    실행하기 전 예상을 해보면 객체 내부에서 사용되는 this는 해당 객체를 가리키게 된다.

    아, 그렇다면 각 메서드를 호출할 때 둘 다 hello! john이 출력되야 하지 않을까?!

     

     

    obj1.hello()를 호출하면 this는 obj1을 가리키기 때문에 예상대로 hello! john이 출력된다. 

    obj1의 hello 메서드를 obj2의 hello에 할당했지만, obj2.hello()를 호출할 때 amy가 출력되는 이유는

    메서드 호출 시 호출 주체는 함수명 앞에 있는 객체 즉, 점('.') 앞에 있는 객체가 this가 된다.

     

    이것이 자바스크립트에서 메서드가 호출될 때 this가 결정되는 방식이며 호출하는 시점의 객체가 this로 사용된다.

     

    🔎 함수와 메서드의 차이

    더보기

    간단히 말하자면, 함수는 어디서나 호출될 수 있지만, 메서드는 객체에 속한 함수를 가리킨다고 알면 된다.

    그렇기 때문에 모든 메서드는 함수이지만, 모든 함수는 메서드가 아니다.

    함수는 객체에 속하지 않을 수도 있지만, 메서드는 항상 객체에 속하기 때문

     

    그렇다면 둘 다 hello! john이 출력되게 하려면 어떻게 해야할까?

     

    bind 메서드

    bind는 this값을 고정시켜 주는 역할을 한다.

    let obj1 = {
        name: 'john',
        hello: function() {
            console.log('hello! ' + this.name);
        }
    }
    
    let obj2 = {
        name: 'amy',
        hello: obj1.hello
    }
    
    let bindName = obj2.hello.bind(obj1); //this가 가리킬 객체를 obj1로 지정
    
    obj1.hello();
    bindName();

    bindName 변수는 obj2.hello가 obj1의 hello 메소드를 참조하지만,

    bind 메소드를 사용해서 함수가 호출될 때 this가 특정 객체(obj1)를 가리키도록 설정한 새로운 함수를 만들었기 때문에

    this는 항상 obj1을 가리키게 된다.

     

    그래서 obj1.hello()와 bindName()을 호출하면 둘 다 hello! john이 출력되게 된다.

     

    화살표 함수

    일반 함수는 어디서 어떻게 호출되는지에 따라 this가 결정됐지만,

    화살표 함수는 this가 존재 하지 않아 상위 this에 접근하게 된다.

    let person = {
        name: 'john',
        age: 12,
        myFunc: function() {
            intro = function() {
                console.log(`제 이름은 ${this.name}이고 나이는 ${this.age}살 입니다.`)
            }
            intro();
        }
    }
    person.myFunc();
    // 제 이름은 이고 나이는 undefined살 입니다.

    intro 함수가 객체 안에 속해있어도 일반 함수로 선언되었기 때문에 intro 함수가 호출되어도 this는 전역 객체(window)를 가리키게 된다.

    전역 객체는 name과 age 속성을 갖고 잊지 않기 때문에 this.name과 this.age는 undefined가 된다.

     

    그렇다면 intro함수를 화살표 함수로 쓰면

    let person = {
        name: 'john',
        age: 12,
        myFunc: function() {
            intro = () => {
                console.log(`제 이름은 ${this.name}이고 나이는 ${this.age}살 입니다.`)
            }
            intro();
        }
    }
    person.myFunc();
    //제 이름은 john이고 나이는 12살 입니다.

    화살표함수는 자신의 상위 스코프에서 this 값을 상속받는다.

    그래서 화살표함수를 사용해서 intro 함수를 정의하면 this가 myFunc 메소드의 this를 상속받게 되어 해당 객체의 속성에 접근하기 때문에 this.name과 this.age는 person 객체의 속성을 정상적으로 참조할 수 있다.

     

    댓글