상세 컨텐츠

본문 제목

#9. This

내일배움캠프 학습/JavaScript

by 남민우_ 2024. 11. 8. 20:40

본문

이번 항목에서는 'This' 의 정의, 활용 방법, 바인딩 Call/Apply/Bind 에 대해서 학습한다.

먼저 이 This 란 무엇일까.

This

코드에서의 This 는 어떠한 객체를 가리킬 때 사용한다.

C/C++ 에서 This 는 자기 자신, 클래스 등 스스로를 가리킬 때 사용되었지만 JS 에서는 그 활용이 조금 다르다.

이 This 는 항상 런타임에 결정된다. 런타임이랑 코드가 돌아가는 환경으로, JS에는 1. 노드, 2. 브라우저 의 두가지 환경이 있다.

 

먼저 브라우저에서의 this 를 찾아보면

다음과 같이 Window 를 가리키는 것을 알 수 있다.

저 console.log(this) 는 특정한 함수 내부나 함수 표현식으로 구현된 코드가 아니니 전역 컨텍스트 항목이라고 볼 수 있고 그 말은 전역환경에서의 this 는 코드에서는 (global) 객체, 브라우저에서는 (Window) 객체를 나타낸다는 것을 뜻한다.

 

코드에서의 this는 또 다시 두가지 경우로 나눌 수 있다.

함수와 메서드 두 경우로 나눠야 하는데, 이 둘은 비슷해보이지만 '독립성' 개념에서 차이가 있다.

1. 함수 : 스스로 실행 가능 - 독립적, this = 전역 객체가 된다.

2. 메서드 : 객체 . 메서드명() 과 같이 이 메서드를 호출할 객체가 있어야 한다 - 의존적, this = 호출 객체가 된다.

 

코드로 살펴보면 다음과 같다.

// CASE1 : 함수
var func = function (x) {
	console.log(this, x);
};
func(1); // Window { ... } 1 <- this : 전역객체

// CASE2 : 메서드
var obj = {
	method: func,
};
obj.method(2); // { method: ƒ } 2 <- this : obj

JS에서의 this 는 이렇게 호출을 '누가' 했는지에 대한 정보가 담긴다.

함수는 누군가 호출하는 것이 아니라 스스로 시행되는 것이므로 전역 객체이며 메서드 내부에서 시행되는 함수의 this 또한 마찬가지로 전역객체이다.

함수로서의 this 호출은 이 함수가 어디에서 시행되냐에 상관없이 무조건 전역객체라고 보면 될 것이다.

 

코드를 하나 더 들여다보자.

var obj1 = {
    outer: function()
    {
        console.log("TEST -> ", this);
        var innerFunc = function() 
        {
            console.log("TEST -> ", this);
        };
        innerFunc();

        var obj2 = 
        {
            innerMethod : innerFunc
        };
        obj2.innerMethod();
    }
};
obj1.outer();

실행 결과는 다음과 같이 나온다.

첫번째 this 는 obj1.outer() 로 obj1 의 내부 메서드로서 호출된다. 해서 이 this는 outer 를 가리킨다.

두번째 this 는 innerFunc() 로 함수가 자체적으로 호출한다. 해서 이 this 는 전역객체를 가리킨다.

 

이러한 this 의 구조가 이해는 가지만, 개발자 입장에서는 메서드 호출 방식이 아니다 라는 이유만으로 무조건 전역객체를 가리킨다는 것이 납득하기 어려울 것이다.

해서 JS에서는 이 this 지칭을 우회하는 방법을 알려준다.

 

This 우회

1. 변수를 활용하여 우회

내부 스코프에 이미 존재하는 this 를 별도의 변수에 할당하여 이 변수를 활용하는 방법이다.

var obj1 = {
	outer: function() {
		console.log(this); // (1) { outer: ƒ }

		// AS-IS
		var innerFunc1 = function() {
			console.log(this); // (2) 전역객체
		}
		innerFunc1();

		// TO-BE
		var self = this;
		var innerFunc2 = function() {
			console.log(self); // (3) { outer: ƒ }
		};
		innerFunc2();
	}
};

// 메서드 호출 부분
obj1.outer();

코드를 보면 this 를 self 변수에 할당하고, 이후 출력을 this 가 아니라 self 로 하고 있다.

이 self 는 객체를 가키리는 것을 확인할 수 있다.

 

2. 화살표 함수 활용

앞서 언급했던 ES6 업데이트에서 이 this가 전역객체를 가리키는 문제 때문에 화살표 함수를 도입했다고 봐도 무방하다고 한다.

일반 함수와 화살표 함수의 차이가 무엇이길래? 라고 한다면 'This Binding' 여부 라고 답할 수 있다.

일반 함수는 바인딩 과정을 거친다면, 화살표 함수는 이를 진행하지 않는다.

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

obj.outer();

(1) 에서의 this 는 obj 가 맞다.

(2) 에서의 this 는 화살표 함수를 사용해, this 바인딩 과정을 거치지 않아서 이전의 this 객체를 그대로 유지한다

해서 이전 (1) 에서 가리켰던 객체, obj 를 그대로 가리킨다.

 

콜백 함수 호출 시 그 함수 내부에서의 This

콜백 함수도 결국에 함수다. 따라서 콜백 함수에서의 this 호출 또한 전역 객체를 가리킨다.

다만 예외가 존재하는데, 콜백 함수에 별도로 this 를 지정한 경우가 있다.

// 별도 지정 없음 : 전역객체
setTimeout(function () { console.log(this) }, 300);

// 별도 지정 없음 : 전역객체
[1, 2, 3, 4, 5].forEach(function(x) {
	console.log(this, x);
});

// addListener 안에서의 this는 항상 호출한 주체의 element를 return하도록 설계되었음
// 따라서 this는 button을 의미함
document.body.innerHTML += '<button id="a">클릭</button>';
document.body.querySelector('#a').addEventListener('click', function(e) {
	console.log(this, e);
});

여기서 addEventListener() 함수가 이 지정의 역할을 한다. 이 함수를 호출한 주체를 this 에 할당하며, 이 코드에서는 Html 코드에 따라 <button> 을 가리킨다.

 

생성자 함수 내부에서의 This

이 경우에는 이 인스턴스, 그 객체를 가리킨다.

var Cat = function (name, age) {
	this.bark = '야옹';
	this.name = name;
	this.age = age;
};

var choco = new Cat('초코', 7); //this : choco
var nabi = new Cat('나비', 5);  //this : nabi

 

 

명시적 This Binding

이 this 바인딩 과정에서 직접 this 가 가리켜야 할 객체를 알리는, 명시적 this 바인딩이 있다.

그 함수에 Call, Apply, Bind 가 있는데 하나씩 살펴보자.

1. Call

var func = function (a, b, c) {
	console.log(this, a, b, c);
};

// no binding
func(1, 2, 3); // Window{ ... } 1 2 3

// 명시적 binding
// func 안에 this에는 {x: 1}이 binding
func.call({ x: 1 }, 4, 5, 6}; // { x: 1 } 4 5 6

func(1,2,3) 의 경우, this에 따로 바인딩을 하지 않아 전역 객체 window { ~ } 를 가리킨다.

이후 func.call() 의 경우, bind 하고 싶은 객체를 call 함수의 매개변수 중 첫번째, { x : 1 } 이라고 명시적으로 알렸다.

따라서 이때의 this 는 { x : 1 } 이 된다.

 

호출 주체가 있는 경우에도 바인딩이 가능하다.

var obj = {
    a : 1,
    method : function(x, y)
    {
        console.log(this.a, x, y);
    }
};
obj.method(2, 3);

이 method 함수는 obj 의 메소드로 사용되어 이때의 this 는 항상 obj 를 가리킨다

따라서 출력은 1, 2, 3 으로 찍히는데, 여기서도 call 함수 사용을 통해 명시적 바인딩이 가능하다.

obj.method.call({a : 4}, 5, 6);
//출력 : 4, 5, 6

method 함수에 call 함수를 더 사용하여 this 를 { a : 4 } 라고 명시적으로 알렸다.

따라서 출력은 4, 5, 6 이 찍히게 된다.

이 call 과 같은 기능을 하는 함수로 두번째 Apply 가 있다.

2. Apply

모든 동작은 전부 call 함수와 동일하다.

차이는 this 로 할당되는 매개변수 뒤의 값들을 [ ] 대괄호로 묶어주기만 하면 된다.

obj.method.apply({a : 4}, [5, 6]);

 

이러한 this binding 을 사용하면 더 유용한 것들이 많이 있는데, 가장 먼저 '유사배열객체' 에서의 경우를 말할 수 있다.

 

유사배열 객체

말 그대로 배열과 유사한 객체를 말한다.

배열의 특성이라고 하면 요소들의 순번인 Index, 요소들의 총 길이인 Length 를 가지는데, 이 유사배열객체 또한 이 값들을 필요로 한다.

//객체에는 배열 메서드를 직접 적용할 수 없어요.
//유사배열객체에는 call 또는 apply 메서드를 이용해 배열 메서드를 차용할 수 있어요.
var obj = {
	0: 'a',
	1: 'b',
	2: 'c',
	length: 3
};
Array.prototype.push.call(obj, 'd');
console.log(obj); // { 0: 'a', 1: 'b', 2: 'c', 3: 'd', length: 4 }

var arr = Array.prototype.slice.call(obj);
console.log(arr); // [ 'a', 'b', 'c', 'd' ]

기존 객체처럼 단순 push 나 slice 같은 메서드를 그냥 사용할 수는 없고, 예시 코드처럼 그 뒤에 call 이나 apply 메서드를 한번 더 붙이는 방식으로 사용한다.

이러한 call / apply 함수들을 '즉시 실행 함수' 라고 부르며 이 때문에 push 와 같은 객체 메서드들을 활용할 수 있게 해준다.

 

아니면 이 유사배열객체를 진짜 배열로 바꿀 수도 있다.

// 유사배열
var obj = {
	0: 'a',
	1: 'b',
	2: 'c',
	length: 3
};

// 객체 -> 배열
var arr = Array.from(obj);

// 찍어보면 배열이 출력됩니다.
console.log(arr);

 

3. Bind 메서드

간단하게, this 를 바인딩해주는 메서드이다.

call, apply 와는 다르게 즉시 호출 함수는 아니라 새 함수에 넣어서 return 하는 형식으로 사용한다.

이 메서드는 함수에 this 를 미리 적용하거나, 함수의 부분만을 적용하고자 함에 따라 사용한다.

 

1. This 미리 적용

var func = function (a, b, c, d)
{
	console.log(this, a,b,c,d);
};

//함수에 this 를 미리 적용
var bindFunc1 = func.bind({x:1});
bindFunc1(5,6,7,8);
//this = { x : 1 }

 

2. 부분 적용

// this 뿐만 아니라 인자 중 일부에 4, 5를 미리 할당
var bindFunc2 = func.bind({x:1}, 4, 5);
bindFunc(6,7);
//출력 : {x:1}, 4,5,6,7

'내일배움캠프 학습 > JavaScript' 카테고리의 다른 글

#11. 콜백 함수 (Call Back Func)  (6) 2024.11.12
#10. 3주차 숙제  (1) 2024.11.08
#8. 실행 컨텍스트  (3) 2024.11.07
#7. 데이터 타입 심화  (6) 2024.11.06
#6. 숙제 - 문자열 임의대로 정렬  (0) 2024.11.05

관련글 더보기