자바스크립트를 하면서 제일 헷갈리고 알기 쉽지 않은 this에 대해서 알아보기로 했다.
어렵지만 한 번쯤은 제대로 정리하고 넘어가야 좋을 것 같다.
자바에서 this는 클래스의 인스턴스 자신을 가리키며 멤버 변수에 접근할 수 있었다.
자바스크립트에서 this는 뭔가 비슷하면서도 다른 것 같다.. 뭘까?
공식 사이트를 통해서 this에 대한 설명을 찾아봤다.
몇 가지 키워드를 살펴보면
1. this의 값은 함수를 호출한 방법에 의해 결정되고, 호출할 때마다 다를 수 있다는 점
2. ES5에서는 bind 메서드를 통해 this 값을 설정할 수 있다는 점
3. ES6에서는 스스로의 this 바인딩을 제공하지 않는 화살표 함수가 추가된 점
세 가지의 특징을 확인할 수 있었다.
하나씩 살펴봐야겠다.
this의 값은 함수를 호출한 방법에 의해 결정된다고 한다.
console.log(this) // window
function foo() {
console.log(this); // window
}
foo();
위 두 가지 예제는 같은 값을 출력한다.
this의 값은 전역 객체인 window를 가지고 있다.
빈 스크립트 공간에서 this를 참조하거나 함수를 만들고 호출했을 때도 this의 값은 window이다.
어떤 특징이 있는 걸까?
var a = 0;
function abc() { var ab = 1; }
console.log(this.a); // 0
console.log(this.abc); // ƒ abc() { var ab = 1; }
위의 예제로 변수 a와 함수 abc는 window의 프로퍼티가 된 것을 알 수 있었고
전역 공간에서 선언하면 window 객체에 포함된다는 사실을 확인할 수 있었다.
새로운 특징은 변수 ab가 window 객체에서 보이지 않는다는 점..!
자바스크립트는 기본적으로 함수 스코프이므로 함수 안에서는 전역 공간이 아닌 새로운 공간이기 때문이다.
하지만 함수를 실행하는 주체는 window 객체이므로 함수 안에 this는 window를 가리킨다.
그렇다면 객체 안에 함수를 만들어서 호출한다면?
var obj = {
foo : function() {
console.log(this); // {foo: ƒ}
}
}
obj.foo();
객체의 메서드로서 함수를 호출하게 되면 this 값이 window 전역 객체가 아닌 obj 객체로 바뀌게 된다.
또 다른 케이스도 있다.
new 연산자로 함수를 호출한다면?
function Foo() {
this.a = 1;
console.log(this);
}
var f = new Foo(); // Foo {a: 1}
new 연산자로 생성자 함수를 호출하게 되면 this 값이 생성자 함수 내 새로운 객체로 바뀌게 된다.
함수 호출 방식에 의해서 결정되는 부분이 1번 키워드의 의미라고 볼 수 있다.
일반 함수, 객체 메서드, new 연산자와 같은 함수 호출 방식에 따라 this의 값은 달라진다!
* use strict 모드에서 일반 함수호출 방식은 this 값이 window가 아닌 undefined를 가리킨다.
두 번째 키워드인 bind에 대해서도 알아봤다.
bind를 알아보니 call과 apply가 함께 언급되는데 함께 정리해봐야겠다.
var obj = {
num : 2,
getNum : function() {
return this.num;
}
}
// case 1 : 객체 메서드 호출
obj.getNum(); // 2
// case 2 : 일반 함수 호출
var getFunc = obj.getNum;
getFunc(); // undefined
위 예제로 객체의 메서드인데도 호출 방식에 따라 this 값이 차이가 나는 걸 확인할 수 있다.
case 2번에서 정상적인 값이 나오도록 하려면 어떻게 해야 할까?
var obj = {
num : 2,
getNum : function() {
return this.num;
}
}
var getFunc = obj.getNum; // obj.getNum.bind(obj)도 가능함
var bindFunc = getFunc.bind(obj);
bindFunc(); // 2
this가 의도와 달리 window를 가리키는 상황이고 이를 해결하기 위해 bind를 활용할 수 있다.
bind를 통해 obj 객체를 연결하여 this 값을 변경하였고
의도하던 값이 정상 출력되었다.
bind는 첫 번째 인수의 객체로 this를 변경해주고 함수를 반환한다.
그럼 call과 apply는 무엇일까?
var obj = {
num : 2,
getNum : function() {
console.log(arguments); // 4,5,6
return this.num;
}
}
var newObj = {
num : 123
}
var getFunc = obj.getNum;
getFunc.call(newObj, 4,5,6); // 123
getFunc.apply(newObj, [4,5,6]); // 123
call과 apply는 bind와 마찬가지로 첫 번째 인수에 this 값으로 사용될 객체를 지정한다.
두 번째 인수에는 함수에 전달할 인수를 설정할 수 있다.
call은 인수 목록을 받고 apply는 인수 배열을 받는 점에 차이가 있다.
bind와 call/apply의 차이는 bind는 this 값을 지정하고 함수를 반환하지만
call과 apply는 this 값 지정과 인수 전달, 함수 실행이 이루어지는 게 큰 차이점이다.
세 번째 키워드인 ES6의 화살표 함수에 대해서도 알아봤다.
화살표 함수(arrow function)에 대한 공식 설명이다.
간편하지만 몇 가지 제한이 있는 점이 보이는데, 위에서 배운 this 바인딩과 call/apply/bind 관련 내용도 확인된다.
위의 설명과 실무에서 사용되는 것을 종합해보면 화살표 함수는 콜백 함수에 주로 사용된다.
var obj = {
a : 1,
foo : function() {
var fn = () => { console.log(this.a) }
fn(); // 1 (this : obj)
function fn2() { console.log(this.a) }
fn2(); // undefined (this : window)
}
}
obj.foo();
간단한 예제로 화살표 함수 fn과 일반 함수 fn2를 비교해봤다.
foo 함수는 객체의 메서드로 호출되었으므로 this의 값은 obj 객체이다.
하지만 foo 함수 안에서 또 다른 함수들의 this는 window 일 것이다.
그런데 화살표 함수 fn과 일반 함수 fn2는 this 값이 다르다.
무슨 차이일까?
화살표 함수는 자신의 this가 없습니다. 대신 화살표 함수를 둘러싸는 렉시컬 범위(lexical scope)의 this가 사용됩니다.
화살표 함수는 일반 변수 조회 규칙(normal variable lookup rules)을 따릅니다.
때문에 현재 범위에서 존재하지 않는 this를 찾을 때, 화살표 함수는 바로 바깥 범위에서 this를 찾는 것으로 검색을 끝내게 됩니다.
화살표 함수의 설명 중 this에 대한 부분을 가져왔다.
정리하자면 기본적으로 화살표 함수의 this는 없고 상위 스코프에서 this를 찾아서 참조한다.
위 예제에 적용해보면 fn의 상위 스코프인 foo 함수의 this 값을 참조한 것이다.
화살표 함수의 가장 큰 특징이라고 할 수 있다.
자바스크립트에서 꼭 알고 넘어가야하는 this에 대해서 알아봤다.
어려웠지만 공부하면서 정리해놓으니 많은 도움이 된 것 같다.
'JavaScript' 카테고리의 다른 글
[JavaScript] for in, for of 차이 알아보기 (0) | 2022.07.17 |
---|