Sh개발일기

자바스크립트 this 본문

웹개발

자바스크립트 this

Sh4869 2021. 1. 12. 22:37

본 포스팅은 "코어 자바스크립트, 정재남" 책을 읽고 요약한 내용입니다.

this

자바스크립트에서 this는 기본적으로 실행 컨텍스트가 생성될 때 결정된다.

실행 컨텍스트는 함수를 호출하며 생성되므로 결국 this는 함수를 호출할 때 결정되는 것이다.

이 때 함수를 어떤식으로 호출하는지에 따라 this의 값이 달라지게 된다.

 

this는 자바스크립트에서 가장 혼란스러운 개념중 하나이다. 상황에 따라 달라지는 this의 값을 정리해보며 좀 더 심화적으로 이해할 수 있도록 해보자.

1. 전역 공간에서의 this

전역 공간에서 this는 전역 객체를 가리킨다. 전역 컨텍스트를 생성하는 주체가 바로 전역 객체이기 때문이다. 전역 객체는 자바스크립트 런타임 환경에 따라 다른 이름과 정보를 가지고 있는데 브라우저에서는 window, 노드 환경에서는 global이다.

// 브라우저 환경에서의 this
console.log(this);
console.log(window);
console.log(this === window); // true

// 노드 환경에서의 this
console.log(this);
console.log(global);
console.log(this === global); // true

 

2. 메서드로 호출할 때 메서드 내부에서의 this

함수 vs 메서드

함수와 메서드를 구별하는 유일한 차이는 립성에 있다. 함수는 그 자체로 독립적인 기능을 수행하는 반면, 메서드는 자신을 호출한 대상 객체에 관한 동작을 수행한다. 자바스크립트는 상황별로 this키워드에 다른 값을 부여하여 이를 구현한다.

 

자바스크립트에서는 어떻게 할당되었느냐가 아니라 어떻게 호출되었느냐에 따라 함수로서 호출인지 메서드로서 호출인지가 결정되고, this값이 달라진다.

 

다음 코드를 살펴보자

let func = function(x) {
	console.log(this,x);
};
func(1); // Window {...} 1

let obj = {
	method: func
}

obj.method(1); // { method:f } 2

위 코드에서 함수 func는 변하지 않았지만, func를 어떻게 호출하느냐에 따라 this의 값이 달라졌다. 즉, 함수가 할당되어 있는 형태가 아니라, 호출하는 방식에 의해 this가 달라지게 되는것이다.

 

일반적으로 함수로서 호출하는 경우 this는 전역객체를 가리키게 되며, 메서드로서 호출할 경우 호출한 주체에 해당하는 객체가 담기게 된다. 호출한 주체는 함수명 앞의 객체이다(위 코드에서는 obj)

 

메서드로서 함수를 호출하는 방법은 점(.)을 이용하거나 대괄호 표기법(obj['method']) 등을 이용하여 호출하는 것이다.

 

즉, 어떤 함수를 호출할 때 함수의 이름 앞에 객체가 명시되어 있는 경우 메서드로 호출한 것이고, 그렇지 않은 경우에는 모두 함수로 호출한 것이다.

3. 함수로서 호출할 때 함수 내부에서의 this

어떤 함수를 함수로서 호출할 경우 this가 지정되지 않아 전역 객체를 바라보게 된다. 더글라스 크락포드는 이를 명백한 설계상의 오류라고 표현했다. 그리고 이러한 특성은 메서드의 내부함수에서도 마찬가지로, 초심자들이 this에서 가장 혼란을 느끼는 부분중 하나이다.

메서드 내부함수에서의 this

메서드 내부에서 정의하고 실행한 함수에서의 this는 일반적으로 생각하는 방향과 다르게 동작하게 된다. 간단한 코드를 살펴보자.

let obj1 = {
	outer: function() {
    	console.log(this); // (1)
        let innerFunc = function() {
        	console.log(this); // (2) (3)
        }
        innerFunc(); // 내부함수 호출
        
        let obj2 = {
        	innerMethod: innerFunc
        };
        obj2.innerMethod();
    }
};
obj1.outer();

위 코드에서의 this가 가리키는 값을 예측해보자. 결과는 (1) obj1, (2) 전역객체, (3) obj2이다.

this에 이상하지 않은 사람이라면 2번의 결과가 매우 의아할 수 있다. 자세하게 살펴보자.

 

(1)번의 경우는 outer함수를 호출할 때 obj1.outer()의 형식으로 호출하였다. 즉, outer는 메서드로서 호출되었으며 그 결과에 따라 호출의 주체인 obj1객체가 나오게 된다.

 

(2)번의 경우 예상되는 결과값은 obj1이지만 결과는 전역객체이다. 자바스크립트에서의 함수/메서드 호출은 위치에 따라 결정되는것이 아니라 호출하는 방법에 따라 결정되기 때문이다. 위의 코드에서 innerFunc를 호출하는 경우 앞에 아무런 장치도 없이 호출되었으므로, innerFunc는 함수로서 호출되는 것이다. 그리고 함수로서 호출되는 경우 this는 전역객체를 가리키게 된다.(이 부분이 바로 설계상의 오류라고 불리는 지점이다)

 

(3)번의 경우 innerMethod에 innerFunc가 들어갔지만, obj2.innerMethod()의 형식으로 호출되었기에 호출하는 주체인 obj2가 출력된다.

 

그러니 this바인딩에는 함수를 실행하는 당시의 주변 환경(메서드 내부인지, 함수 내부인지 등)은 중요하지 않고, 오직 해당 함수를 호출하는 구문 앞에 점 또는 대괄호 표기가 있는지 없는지가 관건인 것이다.

메서드의 내부 함수에서 this를 우회하는 방법

호출 주체가 없을 경우 자동으로 전역객체를 바인딩하지 않고 호출 당시의 주변 환경의 this를 그대로 상속받아 사용할 수 있다면 좋을것이다. 의미상으로도 훨씬 자연스럽고 이렇게 동작하는 편이 스코프 체인과의 일관성을 지키는 방법일 것이다.

 

ES5까지는 자체적으로 내부함수에 this를 상속할 방법이 없어 self, _this, that등의 이름을 가진 변수를 활용하여 우회하는 방식을 사용하였다.

let obj = {
	outer: function() {
    	console.log(this); // (1) { outer: f }
        let innerFunc1 = function() {
        	console.log(this); // (2) Window { ... }
        };
        innerFunc1();
        
        let self = this;
        let innerFunc2 = function() {
        	console.log(self);
        }
        innerFunc2();
    }
};
ojb.outer();

innerFunc1의 내부에서 this는 전역객체를 가리킨다. 한면 outer 스코프에서 self라는 변수에 this를 저장한 상태에서 호출한 innerFunc2의 경우 self에 객체 obj가 출력된다. 

 

ES6부터는 함수 내부에서 this가 전역객체를 바라보는 문제를 보완하고자, this를 바인딩하지 않는 화살표 함수를 새로 도입했다. 화살표 함수는 실행 컨텍스트를 생성할 때 this 바인딩 과정 자체가 빠지게 되어, 상위 스코프의 this를 그대로 활용 할 수 있다. 

let obj = {
	outer: function() {
    	console.log(this); // (1) { outer: f }
        let innerFunc = () => {
        	console.log(this); // (2) { outer: f }
        };
        innerFunc();
   	}
};
obj.outer();

위의 경우 innerFunc는 화살표 함수를 사용하였기에 this가 outer를 가리키게 된다.

그 밖에도 call, apply등의 메서드를 활용해 함수를 호출할 때 명시적으로 this를 지정하는 방법이 있다.

4. 콜백 함수 호출 시 그 함수 내부에서의 this

콜백 함수에서의 this는 "무조건 이거다!"라고 정의할 수 없다. 콜백 함수의 제어권을 가지는 함수(메서드)가 콜백 함수에서의  this를 무엇으로 할 지 결정하며, 특별히 정의하지 않는다면 기본적인 함수와 마찬가지로 전역객체를 바라보게 된다.

5. 생성자 함수 내부에서의 this

어떤 함수가 생성자 함수로서 호출된 경우(new) this는 새로 만들 인스턴스 자신이 된다.

let Cat = function(name, age) {
	this.bark = '야옹';
    this.name = name;
    this.age = age;
};
let choco = new Cat('초코',7);
let nabi = new Cat('나비',5);
console.log(choco, nabi);

/* 결과
Cat { bark: '야옹', name: '초코', age: 7 }
Cat { bark: '야옹', name: '나비', age: 5 }
*/

 

출처 - 정재남, 『코어 자바스크립트』, 위키북스(2019)

'웹개발' 카테고리의 다른 글

콜백 함수  (0) 2021.01.14
비동기처리  (0) 2021.01.14
이벤트 버블링, 캡쳐링, 위임  (0) 2021.01.14
브라우저 저장소  (0) 2021.01.14
URL / URI / Restful API 1  (0) 2020.11.12