JS this

불꽃남자·2020년 9월 16일
0

this

오늘은 JS의 this 키워드에 대해서 알아본다.

What is this?

MDN의 this에 관한 글에선 JS의 this에 대해 이렇게 설명하고 있다.

대부분의 경우 this의 값은 함수를 호출한 방법에 의해 결정됩니다. 실행중에는 할당으로 설정할 수 없고 함수를 호출할 때 마다 다를 수 있습니다.

실행 컨텍스트(global, function 또는 eval)의 프로퍼티는 비엄격 모드에서 항상 객체를 참조하며, 엄격 모드에서는 어떠한 값이든 될 수 있습니다

this의 값은 함수를 호출한 방법에 의해 결정되며, 비엄격 모드의 어떤 실행 컨텍스트이든 항상 this라는 객체를 참조한다라는 설명이다.

전역 문맥에서의 this

전역 문맥에서 this는 전역 객체를 참조한다.

console.log(this); //window
console.log(this === window); //true

var a = 1;
console.log(this.a); //1
console.log(window.a); //1
console.log(this.a === window.a); //true

디스 1

함수 문맥에서의 this

일반 함수에서

function myFunc() {
     console.log(this);
}
myFunc(); //window

디스 2
전역 문맥에서 단순히 함수를 호출했을 때 함수 안에서 this는 전역 객체를 참조한다.

객체의 메서드에서

함수를 객체의 메서드로 호출했을 때 그 함수 안에서 this는 호출 한 객체를 참조한다.

var obj = {
     a: 5,
     objFunc: function() {
     	console.log(this);
     }
}
var obj2 = { a: 10 };
obj2.obj2Func = obj.objFunc;

obj.objFunc(); //obj
obj2.obj2Func(); //obj2

디스 3

객체의 prototype 메서드에서

var o = {
     a: 5, b: 5,
     func: function() { return this.a + this.b }
}
var p = Object.create(o);
p.a = 2;
p.b = 2;

console.log(p.func()); //4
console.dir(p);

디스 4
코드에서 p 객체는 o.prototype.func에 접근하고 있다. o.prototype.func는 this.a와 this.b를 합친 값을 반환한다.
p.func를 실행하면 4를 반환한다. func 메서드를 p가 호출했기 때문에, this는 p를 참조하게 되는 것이다.
객체의 prototype 메서드를 호출했을 때의 this는 객체의 메서드를 호출했을 때의 this와 같다.

생성자에서

var C = function(b) {
     this.a = b;
}
C.prototype.a = 5;
var o = new C(7);

console.log(o.a);//7
console.dir(o);

디스 5
생성자에서 this는 생성자가 반환하는 객체를 받는 객체이다.
설명이 조금 어렵게 된다. 코드를 살펴보자.

  1. 생성자 함수 C는 인자 b를 this.a에 할당한다.
  2. C.prototype.a를 선언하고 5를 할당한다.
  3. 생성자 함수 C의 인자로 7을 넣고 반환된 객체를 변수 o에 할당한다.
    3.1 생성자 함수 C는 받은 인자 7을 this.a의 값으로 설정한다.
    3.2 여기서 this는 객체 o가 된다. 생성자 C가 반환하는 객체를 o가 받기 때문이다.
  4. var o = new C(7) 에서 생성자 함수 C를 호출할 때에 this는 o를 참조하기 때문에 o.a = 7이 되었다.

내부함수

결론부터 말하자면 내부함수의 this는 항상 전역객체를 참조한다.

일반 함수의 내부함수에서

function outerFunc() {
     function innerFunc() {
     	console.log(this);
     }
     innerFunc();
}

outerFunc();

디스 6

객체의 메서드의 내부함수에서

var o = {
     outerFunc: function() {
     	innerFunc = function() { console.log(this) };
        innerFunc();
     }
}

o.outerFunc();

디스 7

this를 바인딩하는 함수

Function.prototype의 메서드 중에는 apply, call, bind가 존재한다.
이 세 함수의 공통점은 사용자가 this를 바인딩할 수 있게 해준다는 것이다.
이제 하나하나 알아보겠다.

apply 함수

MDN의 apply 함수에 관한 글에서는 apply에 대해 이렇게 설명하고 있다.

apply() 메서드는 주어진 this 값과 배열 (또는 유사 배열 객체) 로 제공되는 arguments 로 함수를 호출합니다.

apply는 함수를 호출하며 첫 번째 인자로 받은 객체를 호출된 함수에 바인딩하고, 두 번째 인자로 받은 배열(유사 배열도 받는다)을 호출된 함수의 인자로 넣어준다.
코드와 같이 살펴보자.

var o = {
    name: "o",
    objFunc: function() { console.log(this); }
}
var b = { name: "b" };

o.objFunc.apply(b);

디스 8

  1. objFunc는 o 객체의 메서드이기 때문에 objFunc안에서 this는 o를 참조한다.
  2. b라는 별도의 객체를 만든 뒤 o.objFunc 메서드를 apply 함수로 호출한다.
    2.1 이 때 apply 함수의 첫 번째 인자로 b를 넣었기 때문에 o.objFunc의 this는 b에 바인딩 된다.
  3. objFunc의 this가 b를 참조하고 있다.

apply의 두 번째 인자도 사용해보자.

var o = {
    name: "o",
    objFunc: function(name, age) { this.name = name; this.age = age; }
}
var b = { name: "b" };

o.objFunc.apply(b, ["a", 1]);
console.dir(b);

디스 9
1. o.objFunc는 인자로 name과 age를 받고, 각각 this.name과 this.age의 값으로 할당한다.
2. b라는 별도의 객체를 만든 뒤 objFunc 메서드를 apply 함수로 호출하고 b에 바인딩한다.
3. apply 함수의 두 번째 인자로 넘긴 단일 배열은 차례대로 objFunc의 인자로 들어간다.

헷갈리기 쉬운 것은 apply 함수는 함수를 반환하는 게 아닌 호출한다는 것이다.

call 함수

call 함수는 apply 함수와 굉장히 닮아있다.
모든 게 apply함수와 똑같지만 단 하나 다른 것은 두 번째 인자로 단일배열을 받는 것이 아니라 인수 목록을 받는다.
예를 들면 apply(this, [1, 2, 3]); 할 것을 call(this, 1, 2, 3); 한다는 것이다.

var o = {
    name: "o",
    objFunc: function(name, age) { this.name = name; this.age = age; }
}
var b = { name: "b" };

o.objFunc.call(b, "a", 1);
console.dir(b);

디스 10

bind 함수

bind는 함수를 반환하며 첫 번째 인자로 받은 객체를 해당 함수에 바인드한다. 그리고 두 번째 인자부터 차례대로 해당 함수의 인자로 넣는다. call 함수처럼.
bind 함수에서 주목해야 할 점은 함수를 호출하는 게 아니라 반환한다는 것이다.

var o = {
    name: "o",
    oFunc: function(name, age) { this.name = name; this.age = age; }
}
var b = { name: "b" };

b.bFunc = o.oFunc.bind(b, "a", 1);
b.bFunc();
console.dir(b);

디스 11
또한 bind 함수가 반환한 함수는 다음과 같은 내부 프로퍼티를 가지고 있다.
디스 12
[[TargetFunction]] : bind한 함수의 원본 객체
[[BoundThis]] : bind한 함수를 실행했을 때 첫 번째 인자로 전달 될 객체
[[BoundArgs]] : bind한 함수를 실행했을 때 두 번째 인자부터 전달 될 배열

마치며

오늘은 this에 대해서 알아보았다. 알아보며 든 생각은 이건 약간 단순암기같다 라는 생각이었다. 여기에서 호출된 this는 무엇을 참조하고, 저기에서 호출된 this는 무엇을 참조하고.. 이런 느낌.
아무튼 내 머릿속에서 막연히 추상적인 형태로 돌아다니던 개념들이 구체적으로 잡히는 것이 아주 좋은 느낌이다. 여태껏 안 알아보고 뭐 했나 싶은 마음이다.

내일은 고대하던 실행 컨텍스트에 대해 알아볼 차례다. 이전에 실행 컨텍스트에 대해 알려고 했을 때는 실패했었다. 하지만 지금의 나는 다르다... 충분히 알 수 있을 것이다.

참고한 사이트

MDN 웹 문서
이웅모님의 웹 프로그래밍 튜토리얼

profile
프론트엔드 꿈나무, 탐구자.

0개의 댓글