[JavaScript] 일반 함수로 호출 시의 this바인딩

qwe8851·2024년 2월 22일
0

📚 JavaScript

목록 보기
51/53

this바인딩은 함수 호출 방식, 즉 함수가 어떻게 호출되었는지에 따라 동적으로 결정이 되는데요, 

주의할 점은 동일한 함수도 다양한 방식으로 호출될 수 있음에 따라 this가 변하게 된다는 점입니다. 

  1. 일반 함수 호출
  2. 메서드 호출
  3. 생성자 함수 호출
  4. Function.prototype.apply/call/bind 메서드에 의한 간접 호출

그중에서도 일반 함수로 호출되었을때의 this에 대해 정리해보겠습니다.

🤔 일반 함수 호출

기본적으로 this에는 전역 객체(global object)가 바인딩 됩니다. 

function foo() {
	console.log("foo's this: ", this); // window
    function bar() {
    	console.log("bar's this: ", this);	// window
    }
    bar();
}
foo();

위 예제처럼 전역 함수(foo)와 중첩 함수(bar)를 일반 함수로 호출하면 함수 내부의 this에는 전역객체가 바인딩됩니다.

하지만 this는 객체 프로퍼티나 메서드르 참조하기 위한 자기 참조 변수이므로 객체를 생성하지 않는 일반 함수에서 this는 의미가 없습니다.

function foo() {
	'use strict';
    
    console.log("foo's this: ", this);	//undefined
    function bar() {
    	console.log("bar's this: ", this);	//undefined
    }
    bar();
}
foo();

그래서 strict mode를 적용한 일반 함수 내부에서 this는 undefined가 바인딩 됩니다. 

메서드 내에서 정의한 중첩 함수도 일반 함수로 호출되면 중첨 함수 내부의 this는 전역 객체가 바인딩 됩니다. 

var value = 1;

const obj = {
	value: 100,
    foo() {
    	console.log("foo's this: ", this);	// {value: 100, foo: f}
        console.log("foo's this: ", this.value);	// 100
        
        // 메서드 내에서 정의한 중첩 함수 
        function bar() {
        	console.log("bar's this: ", this);	// window
        	console.log("bar's this: ", this.value);	// 1
        }
        bar();
    }
};

obj.foo();

위 예시에서 var키워드로 전역 변수 value를 선언했습니다. 

그리고 obj객체는 전역변수 value와 같은 이름을 가진 value 프로퍼티와 foo()메서드를 가집니다. foo()메서드 내에는 중첩함수인 bar()를 가지고 있습니다. 

foo메서드 내에서 정의한 중첩 함수도 일반 함수로 호출되면 중첩 함수 내부의 this는 전역 객체가 바인딩 되고,

콜백함수도 일반함수로 호출된다면 콜백함수 내부의 this도 전역 객체가 바인딩 됩니다.

어떠한 함수라도 일반 함수로 호출되면 this는 전역 객체(window)가 바인딩되는 것이지용

즉 정리해보자면 obj내부에 있는 foo()메서드는 obj를 this로 가지지만 

foo()메서드 내부의 bar()는 일반함수로 호출되어 this가 전역객체인 window가 됩니다. 

이처럼 일반 함수로 호출된 모든 함수(중첩/콜백 함수 포함) 내부의 this는 전역 객체가 바인딩 됩니다. 

그런데 이 처럼 메서드 내의 중첩/콜백의 this가 전역객체를 바인딩하는 것은 문제가 있습니다.

중첩함수 또는 콜백함수는 외부 함수를 돕는 헬퍼 함수의 역할을 수행하므로 외부 함수의 일부 로직을 대신하는 경우가 대부분이라

외부 함수인 메서드와 중첩/콜백 함수의 this가 일치하지 않는 것은 중첩/콜백 함수를 헬퍼 함수로 동작하기 어렵게 만들기 때문입니다. 

위 예제의 경우 메서드 내부의 중첩함수인 bar()에 전달된 this는 전역객체(window)가 바인딩되므로

obj객체의 value프로퍼티가 아닌 전역 객체의 value프로퍼티. 즉, window.value를 참조하기 때문에 콘솔에 1이 찍히게 됩니다.

메서드 내부의 중첨 함수나 콜밸 함수의 this바인딩을 메서드의 this바인딩과 일치시키기 위해서는 대표적으로 3가지 방법이 있습니다. 

1️⃣ 현재 this를 변수에 저장해 참조

var value = 1;

const obj = {
	value: 100,
    foo() {
    	// this바인딩(obj)을 변수 that에 할당
    	const that = this;
        
        // 중첩 함수 내부에서 this대신 that참조
        function bar() {
        	console.log(that.value);	// 100
        }
        bar();
    }
};

obj.foo();

2️⃣ apply/call/bind 메서드 사용

var value = 1;

const obj = {
	value: 100,
    foo() {
        function bar() {
        	console.log(this.value);	// 100
        }
        
        // 1. apply()
        bar.apply(this);

        // 2. call()
        bar.call(this);

        // 3. bind() 
        const boundBar = bar.bind(this);
        boundBar();
    }
};

obj.foo();

3️⃣ 화살표 함수(arrow function)사용

var value = 1;

const obj = {
	value: 100,
    foo() {
        const bar = () => {
        	console.log(this.value);	// 100
        }
    }
};

obj.foo();

📝 Summary

  • 외부함수의 this를 변수로 저장해 참조하기
  • Function.prototype.apply / call / bind 사용하기
  • 화살표 함수(arrow function) 사용하기

🛠️ Reference

모던 자바스크립트 Deep Dive

profile
FrontEnd Developer with React, TypeScript

0개의 댓글