Javascript

Javascript this Binding

코딩딩코 2023. 1. 4.

자바스크립트를 하면서 this를 어떻게 사용하면 원하는 값이 나오질 않고 어떻게 하면 나올 때가 있어 찾아보고

정리를 했습니다.

 

this Binding 

함수가 호출되는 방식에 따라 자바스크립트에서 this가 참조하는 것이 다릅니다.

이것을 this 바인딩이라고 합니다.

 

Defailt Binding (기본 바인딩)

기본적으로 this는 전역객체를 가리킵니다.

node환경에서는 global 객체를, 브라우저에서는 window 객체를 가리킵니다.

또한 함수 내부에서 this를 호출하여도 window 객체를 가리킵니다.

console.log(this);		// window 객체


let a = 10;
window.b = 20;
function log() {
    console.log(this.a);	// undefined
    console.log(this.b);	// 20
}

log();

 

 

 

Implicit Binding(암시적 바인딩)

암시적 바인딩이란, 함수가 객체의 메서드로서 호출되는 상황에서 this가 바인딩되는 것을 의미합니다.

이때 this는 해당 함수를 호출한 객체에 바인딩됩니다.

const object = {
    a: 20,
    log: function() {
    	console.log(this)	// object 객체
        console.log(this.a);	// 20
    }
}

object.log();

 

여기서 주의해야 할 점이 있습니다.

객체의 메서드를 매개변수(콜백)로 넘겨서 실행을 하면 원하는 값이 나오지 않습니다.

const object = {
    a: 20,
    log: function() {
        console.log(this);	// window 객체
        console.log(this.a);	// undefined
    }
}

setTimeout(object.log, 100);

///////////////////////////////////////
function setTimeout(cb, delay) {
  // delay 만큼 기다립니다.
  cb(); // 기본 바인딩, object.log()가 아닌 log()와 같습니다.
}

 

setTimeout 함수 안에 전달한 콜백은 log라는 함수의 레퍼런스일 뿐, object의 콘텍스트를 가지고 있지 않기 때문입니다.

이런 상황을 암시적 바인딩이 소실되었다고 합니다.

즉, 위와 같이 setTimeout 내부에서 호출되는 콜백은 object 객체의 콘텍스트에서 실행되는 것이 아니기 때문에,

this는 기본 바인딩이 적용돼서 전역 객체에 바인딩됩니다.

 

 

또한, 메서드 내부함수 내부에서 this를 호출할 경우, 이는 함수 내부에서 호출한 것이 기본 바인딩이 되어 전역객체를 가리키게 됩니다.

const object = {
    a: 20,
    log: function() {
    	// 객체 안에서 정의 되었기 때문에 '메서드'
        
        console.log(this);	// object 객체
        console.log(this.a);	// 20


        // 메서드 안에서 함수를 정의했기 때문에 '함수'
        const logFunction = function() {
            console.log(this);	    // window 객체
            console.log(this.a);	// undefined
        }

        logFunction();
    }
}

object.log();

 

 

 

New Binding (생성자 바인딩)

자바스크립트의 new 키워드는 함수를 호출할 때 앞에 new 키워드를 사용하는 것으로 객체를 초기화할 때 사용합니다.

이때 사용되는 함수를 생성자 함수라고 합니다.

그리고 생성자 함수에서는 this키워드를 해당 생성자를 이용해 생성할 객체에 대한 참조로 사용합니다.

function Object() {
    this.a = 10;
}

const object = new Object();

console.log(object.a);	// 10

생성된 객체의 a라는 프로퍼티에 10이라는 값이 할당되고, 해당 객체는 object라는 변수에 할당됩니다.

 

 

 

Explicit Binding (명시적 바인딩)

자바스크립트의 모든 함수는 call()apply()bind()라는 메서드를 사용할 수 있습니다.

3가지 메서드 중 하나를 호출함으로써 this 바인딩을 코드에서 명시하는 것을 명시적 바인딩이라고 합니다.

이때 this는 내가 명시한 객체에 바인딩됩니다.

 

call(), apply()

const object = {
    a: 20
}

function log() {
	console.log(this);
    console.log(this.a);
}

log();			// window 객체, undefined
log.apply(object);	// object 객체, 20

log 함수에 프로토타입 메서드 call, apply의 매개변수로 바인딩할 객체를 넘겨주면서

log 함수 안에서 호출된 this는 매개변수로 넘겨준 object 객체로 바인딩을 합니다.

 

 

call과 apply의 동작은 같지만 두 번째 매개변수로 객체의 인자를 전달해 주는데(e.g. 생성자의 매개변수),

call은 매개변수의 목록, apply는 배열을 받는다는 차이점이 있다고 합니다.

 

bind()

const object = {
    a: 20
}

function log() {
    console.log(this);
    console.log(this.a);
}

const test = log.bind(object);

log();	// window 객체, undefined
test();	// object 객체, 20
test();	// object 객체, 20

bind 메서드는 매개변수로 전달받은 오브젝트로 this가 바인딩된 함수를 반환합니다.

이것을 하드 바인딩이라고 하는데 하드 바인딩된 함수는 이후 호출될 때마다 처음 정해진 this 바인딩을 가지고 호출됩니다.

 

 

화살표 함수

화살표 함수(Arrow Function)는 this를 바인딩할 때 앞서 설명한 규칙들이 적용되지 않고,

this에 어휘적 스코프(Lexical scope)가 적용됩니다.

화살표 함수 내부의 this는 언제나 상위 스코프의 객체를 가리키는데, 이를 어휘적 스코프라고 합니다.

 

화살표 함수에는 this라는 변수 자체가 존재하지 않기 때문에 그 상위 환경에서의 this를 참조하게 됩니다.

즉, 화살표 함수를 정의하는 시점의 콘텍스트 객체가 this에 바인딩됩니다.

 

객체에서 화살표 함수

const object = {
    a: 20,
    log: () => {
        console.log(this);	// window 객체
        console.log(this.a);	// undefined
    }
}

object.log();

객체의 메서드를 화살표 함수로 선언할 경우, this는 해당 객체가 아닌 전역 객체를 가리키기 때문에 주의해야 합니다.

 

 

상위 스코프의 객체를 가리키는 화살표 함수

const object = {
    a: 20,
    log: function() {

        const logFunction = function() {
            console.log(this);		// window 객체
            console.log(this.a);	// undefined
        }

        const logArrow = () => {
            console.log(this);		// object 객체
            console.log(this.a);	// 20
        }

        logFunction();
        logArrow();
    }
}

object.log();

메서드 안에 함수를 호출하면 전역을 가리킨다고 했었죠.

그래서 logFunction은 전역을 가리키기에 원하는 값을 가져오지 못하였지만

화살표 함수는 정의하는 시점에서 상위 스코프의 객체를 가리키기 때문에 원하는 값을 가져올 수 있었습니다.

즉, logArrow 함수는 선언 시에 this는 object 객체를 가리키고 있습니다.

 

 

명시적 바인딩이 불가능한 화살표 함수

const test = {
    a: 100
}

const object = {
    a: 20,
    log: function() {

        const logFunction = function() {
            console.log(this);		// test 객체
            console.log(this.a);	// 100
        }

        const logArrow = () => {
            console.log(this);		// object 객체
            console.log(this.a);	// 20
        }

        logFunction.apply(test);
        logArrow.apply(test);
    }
}

object.log();

화살표 함수 내부에서 this를 사용할 경우, this에 바인딩할 객체가 정적으로 결정되기 때문에 call, apply, bind로 this를 변경할 수 없습니다.

그렇기 때문에 주로 콜백 함수로 사용할 때 유용합니다.

 

 

콜백함수로 사용되는 화살표 함수

const object = {
    a: 20,
    log: function() {
        setTimeout(() => {
            console.log(this.a), 100	// 20
        })
        
    },

    log2: function() {
        
        const test = function() {
            console.log(this.a);
        }

        test();			// undefined

        setTimeout(test, 100);	// undefined
    }

    
}

object.log();
object.log2();

setTimeout의 콜백인 화살표 함수의 선언 시에 this는 object 객체를 가리키고 있기 때문에(렉시컬 스코프), 콜백이 실행될 때 this는 object를 가리키게 됩니다.

 

test() 함수는 메서드가 아닌 함수이기 때문에 전역을 가리켜 undefined가 뜨게 됩니다.

 

 

이벤트에서 this

const button = document.querySelector("button");

button.onclick = () => {
    console.log(this);	// window 객체
}

button.onclick = function() {
    console.log(this);	// button 객체
}

addEventListener등과 같은 이벤트를 정의하는 함수에서는 this에 해당 이벤트 리스너가 호출된 엘리먼트가 바인딩되도록 정의되어 있습니다.

이처럼 이미 this의 값이 정해져 있는 함수의 경우, 화살표 함수를 사용하면 기존 바인딩 값이 사라지고 상위 스코프가 바인딩되기 때문에 주의해야 합니다.

상위 스코프의 속성들을 쓰려고 의도한 경우라면 사용할 수 있습니다.

 

 

 

참조

https://seungtaek-overflow.tistory.com/21

 

https://velog.io/@danmin20/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-this-%EB%B0%94%EC%9D%B8%EB%94%A9%EC%9D%B4%EB%9E%80

 

https://velog.io/@padoling/JavaScript-%ED%99%94%EC%82%B4%ED%91%9C-%ED%95%A8%EC%88%98%EC%99%80-this-%EB%B0%94%EC%9D%B8%EB%94%A9

 

감사합니다.

'Javascript' 카테고리의 다른 글

Javascript 와 JQuery  (1) 2023.01.03

댓글