👀 this란?
Java에서 this는 자기 자신을 가리키는 참조 변수로 객체 자신에 대한 참조 값을 의미한다.
주로 매개변수와 객체 자신의 멤버 변수 명이 같을 때, 구분하기 위해 사용된다.
public class Person {
private String name;
public Person (String name) {
this.name = name;
}
}
이런 식으로 멤버 변수에 매개변수를 할당해주기 위해 사용한다.
하지만 JS에서 this 키워드는 다른 언어에서와는 다르게 동작한다!!
(왜 맨날 JS 너만 달라...)
JS에서 this에 바인딩되는 객체는 해당 함수의 호출 방식이나 함수 선언 위치에 따라 달라지게 된다.
어떤 객체가 바인딩 될 지 주의해야 한다는 의미이다.
👀 일반 함수에서 this
- 일반 함수에서 this는 함수의 선언 위치에 관계 없이 함수를 호출하는 방법에 따라 결정된다.
- 함수를 호출한 객체를 가르킨다.
const obj = {
name: "heedonguri",
printName: function () {
console.log(this.name);
}
}
const obj2 = {
name: "guri",
printName: obj.printName
}
obj2.printName(); // guri
obj2.printName() 에서는 obj2가 printName 함수를 호출했으므로 this는 obj2를 가르키게 된다.
🤔 그럼 객체 내부 함수에서 this를 호출하면 어떻게 될까?
const obj = {
name: "heedonguri",
fn: function() {
console.log(this); // obj
function bar() {
console.log(this); // window
}
bar(); // 함수 호출
}
};
obj.fn();
객체의 내부함수에서 this를 출력하면 window 객체가 나온다.
기본적으로 this는 전역 객체인 window 객체이기 때문이다.
bar() 함수는 호출한 객체가 없기 때문에 window 객체가 출력되는 것이다.
호출한 객체가 없다? -> this에는 window 객체가 바인딩 된다는 것을 명심하자!
👀 화살표 함수에서 this
- 화살표 함수에서 this는 함수의 호출 방법에 관계 없이 함수를 선언한 위치에 따라 결정된다.
- 화살표 함수를 감싸는 상위 스코프의 this를 그대로 가져와서 사용한다.
const obj = {
name: "heedonguri",
printName: () => {
console.log(this.name);
}
}
obj.printName(); // undefined
일반 함수를 화살표 함수로 바꿨을 뿐인데 출력 결과가 달라진다.
화살표 함수에서 this는 상위 스코프의 this를 가져다 쓴다고 했는데,
printName가 선언된 obj 객체의 상위 스코프는 전역이므로 window가 된다.
하지만 전역 스코프에는 name이 정의되어 있지 않으므로 undefined가 출력되는 것이다.
내가 원했던 결과는 heedonguri가 출력되는 것이다.
아래와 같이 화살표 함수를 일반 함수로 감싸주면 해결된다.
const obj = {
name: "heedonguri",
printName: function () {
const arrowFn = () => {
console.log(this.name);
};
arrowFn();
}
};
obj.printName(); // heedonguri
일반 함수에서 this는 호출한 객체를 가르킨다고 위에서 말했었다.
따라서 printName 함수의 this는 obj가 된다.
arrowFn은 화살표 함수이고 arrowFn 함수의 상위 스코프는 printName 함수이다.
따라서 arrowFn 함수의 this는 printName 함수의 this와 같은 값을 가진다.
이러한 원리로 "heedonguri"가 출력되는 것이다.
이렇게 사용하는게 어렵다면?
바인딩 할 객체를 지정해주는 방법이 존재한다!
👀 명시적 바인딩(call, apply, bind)
명시적 바인딩 방법을 사용하면 함수 호출 방식과 관계없이 this를 지정할 수 있다.
1. call
- 함수를 호출하면서 this를 바인딩한다.
- 인자를 쉼표로 구분해서 받는다
- printName.call(obj, a, b)
2. apply
- 함수를 호출하면서 this를 바인딩한다.
- 인자를 배열로 받는다
- printName.apply(obj, [1, 2])
call과 apply 차이점은 매개변수를 받는 방식 밖에 없다.
3. bind
- 함수를 호출하지 않고 this를 바인딩한다.
- 바인딩한 새로운 함수를 반환한다.
- const newPrintName = printName(obj, 1, 2)
두 번째 매개변수부터는 새로운 함수의 매개변수로 들어가게 된다. - bind는 한 번만 사용가능하고, 두 번 이상 호출 시 바인딩 되지 않는다.
하지만 명시적 바인딩 방법은 화살표 함수에서는 사용할 수 없다.
화살표 함수는 this 바인딩이 존재하지 않고, 상위 스코프의 this를 그대로 가져와서 사용하기 때문이다.
화살표 함수의 this는 함수가 정의되는 위치에 의해 결정되고 이후에는 바뀌지 않는다.
👀 콜백함수에서 this
const obj = {
name: 'heedonguri',
printName() {
console.log(this.name);
},
};
function callCB(cb) {
cb();
}
callCB(obj.printName); // undefined
위의 코드의 실행 결과는 undefined가 출력된다.
obj.printName 때문에 this에 obj가 바인딩 될 것이라고 예상했을 수 있다.(내가 그랬다..)
실제 printName을 호출하는 되는 때는 cb()가 실행되었을 때이다.
cb()의 this는 전역객체이므로,
callCB의 입장에서는 매개변수로 obj.printName이 들어오나 printName이 들어오나 동일하다.
그냥 obj에 있는 printName 메서드를 호출한 것이고,
name이라는 전역 변수가 없기 때문에 undefined가 출력된 것이다.
만약 obj의 name이 출력되게 하고 싶다면 명시적 바인딩을 사용하면 된다.
const obj = {
name: 'heedonguri',
printName() {
console.log(this.name);
},
};
function callCB(cb) {
cb();
}
callCB(obj.printName.bind(obj)); // heedonguri
printName에 obj 객체를 바인딩 해주게 되면 되면 "heedonguri"가 출력된다.
또는 화살표 함수를 이용하는 방법도 있다.
const obj = {
name: 'heedonguri',
printName() {
console.log(this.name);
},
};
function callCB(cb) {
cb();
}
callCB(() => obj.printName()); // heedonguri
printName이 선언된 상위 스코프는 obj이기 때문에 "heedonguri"가 출력된다.
👀 이벤트 핸들러에서 this
이벤트 핸들러로 등록된 함수의 종류에 따라 달라진다.
document.getElementById('myButton').addEventListener('click', function() {
console.log(this); // 클릭된 버튼
});
document.getElementById('myButton').addEventListener('click', () => {
console.log(this); // window 객체
});
- 일반 함수 : 이벤트를 달아놓은 DOM 요소가 this로 할당된다.
- 화살표 함수 : 상위 스코프인 window 객체를 가르킨다.
일반적으로 내부 함수의 this는 window 객체인데
이벤트 핸들러에서는 이벤트를 발생시킨 요소를 가르킨다는걸 기억하자!
👀 언제 뭘 쓰는게 좋을까?
일반 함수는 this가 호출 시점에 결정된다. 즉, 동적으로 결정된다는 의미이다.
- 이벤트 핸들러 : this는 이벤트를 발생시킨 요소를 가리킨다.
- 메소드 정의 : 메소드 호출 시, this가 메소드를 호출한 객체를 가리킨다.
화살표 함수는 상위 스코프의 this를 상속받는다.
- 콜백함수 : setTimeout, setInterval 같은 메소드에서 화살표 함수를 사용하면 상위 스코프의 this를 유지한다.
const obj = {
name: 'Object',
method: function() {
setTimeout(function() {
console.log(this);
}, 100);
}
};
obj.method(); // window 객체
const obj = {
name: 'Object',
method: function() {
setTimeout(() => {
console.log(this);
}, 100);
}
};
obj.method(); // obj
'HTML,CSS,JS' 카테고리의 다른 글
[JS] Prototype (0) | 2024.08.08 |
---|---|
[JS] closure (0) | 2024.08.07 |
[JS] 동작원리 (0) | 2024.08.05 |
[CSS] 리플로우와 리페인트(Reflow, Repaint) (0) | 2024.07.08 |
[JS]Reduce 함수 (4) | 2024.04.12 |