๐ this๋ ๋ฌด์์ธ๊ฐ?
this๋ ์๋ฐ์คํฌ๋ฆฝํธ์์ ์์ ์ด ์ํ ๊ฐ์ฒด ๋๋ ์์ ์ด ์์ฑํ ์ธ์คํด์ค๋ฅผ ๊ฐ๋ฆฌํค๋ **์๊ธฐ ์ฐธ์กฐ ๋ณ์(self-reference variable)**์ ๋๋ค.
๐ ํต์ฌ ๊ฐ๋
- this ๋ฐ์ธ๋ฉ์ ํจ์ ํธ์ถ ๋ฐฉ์์ ์ํด ๋์ ์ผ๋ก ๊ฒฐ์ ๋ฉ๋๋ค
- "๋๊ฐ ๋๋ฅผ ๋ถ๋ ๋" - ์ ์ธ์ด ์๋ ํธ์ถ์ ๋ฐ๋ผ ๋ฌ๋ผ์ง
- ํจ์๋ฅผ ์คํํ๋ ํ๊ฒฝ(ํธ์ถํ๋ ๋ฐฉ๋ฒ, ํ๊ฒฝ)์ด ๋ฌ๋ผ์ง๋ฉด this๋ ๋ณํฉ๋๋ค
๐ฏ this ๋ฐ์ธ๋ฉ์ 4๊ฐ์ง ํจํด
1. ์ ์ญ ํธ์ถ (Global Invocation)
์ ์ญ์์ ํจ์๋ฅผ ํธ์ถํ๊ฑฐ๋ ๋จ๋ ์ผ๋ก this๋ฅผ ์ฌ์ฉํ ๋, this๋ ์ ์ญ ๊ฐ์ฒด๋ฅผ ๊ฐ๋ฆฌํต๋๋ค.
// ๋ธ๋ผ์ฐ์ ํ๊ฒฝ์์ ์ ์ญ this
console.log(this); // Window ๊ฐ์ฒด
var globalVar = 'Global';
var x = this;
console.log(x === window); // true
function globalFunction() {
console.log(this); // Window ๊ฐ์ฒด
console.log(this === window); // true
}
globalFunction();
๐ ์ค๋ช :
- ๋ธ๋ผ์ฐ์ ํ๊ฒฝ: window ๊ฐ์ฒด
- Node.js ํ๊ฒฝ: global ๊ฐ์ฒด
- strict mode์์๋ undefined
2. ๋ฉ์๋ ํธ์ถ (Method Invocation)
๊ฐ์ฒด์ ๋ฉ์๋๋ก ํจ์๋ฅผ ํธ์ถํ ๋, this๋ ํด๋น ๋ฉ์๋๋ฅผ ์์ ํ ๊ฐ์ฒด๋ฅผ ๊ฐ๋ฆฌํต๋๋ค.
// ๊ธฐ๋ณธ ๋ฉ์๋ ํธ์ถ
var person = {
name: 'Kim',
sayHello: function() {
console.log(this); // person ๊ฐ์ฒด
console.log('Hello, ' + this.name); // Hello, Kim
}
};
person.sayHello(); // person ๊ฐ์ฒด๊ฐ this
// ์ค์ฒฉ๋ ๊ฐ์ฒด์์์ ๋ฉ์๋ ํธ์ถ
var company = {
name: 'TechCorp',
employee: {
name: 'Lee',
introduce: function() {
console.log(this); // employee ๊ฐ์ฒด (์ง์ ์ ์ธ ์์ ์)
console.log('I am ' + this.name); // I am Lee
}
}
};
company.employee.introduce(); // employee๊ฐ this
๐ ํต์ฌ: ์ (.) ๋ฐ๋ก ์์ ๊ฐ์ฒด๊ฐ this๊ฐ ๋ฉ๋๋ค.
3. ํจ์ ๋ด๋ถ์ ์ค์ฒฉ ํจ์
๋ฉ์๋ ๋ด๋ถ์ ์ผ๋ฐ ํจ์๋ ์ ์ญ ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํฉ๋๋ค.
const value = 100;
const myObj = {
value: 1,
outerMethod: function() {
console.log('์ธ๋ถ ํจ์ this.value:', this.value); // 1
// ๋ด๋ถ ์ผ๋ฐ ํจ์
const innerFunction = function() {
console.log('๋ด๋ถ ํจ์ this.value:', this.value); // 100 (์ ์ญ)
};
innerFunction();
// ํ์ดํ ํจ์ ์ฌ์ฉ ์
const innerArrowFunction = () => {
console.log('ํ์ดํ ํจ์ this.value:', this.value); // 1 (์ธ๋ถ ์ค์ฝํ ์์)
};
innerArrowFunction();
}
};
myObj.outerMethod();
๐ ํด๊ฒฐ ๋ฐฉ๋ฒ:
- ํ์ดํ ํจ์ ์ฌ์ฉ
- bind() ๋ฉ์๋ ์ฌ์ฉ
- ๋ณ์์ this ์ ์ฅ (var that = this)
4. ์์ฑ์ ํจ์ ํธ์ถ (Constructor Invocation)
new ํค์๋์ ํจ๊ป ํจ์๋ฅผ ํธ์ถํ ๋, this๋ ์๋ก ์์ฑ๋๋ ์ธ์คํด์ค๋ฅผ ๊ฐ๋ฆฌํต๋๋ค.
function Person(name, age) {
console.log(this); // ์๋ก ์์ฑ๋๋ Person ์ธ์คํด์ค
this.name = name;
this.age = age;
this.introduce = function() {
return `์๋
ํ์ธ์, ์ ๋ ${this.name}์ด๊ณ ${this.age}์ด์
๋๋ค.`;
};
}
const person1 = new Person('ํ๊ธธ๋', 30);
const person2 = new Person('๊น์ฒ ์', 25);
console.log(person1.name); // ํ๊ธธ๋
console.log(person2.introduce()); // ์๋
ํ์ธ์, ์ ๋ ๊น์ฒ ์์ด๊ณ 25์ด์
๋๋ค.
๐ ๋ฐ์ธ๋ฉ(Binding)์ด๋?
๋ฐ์ธ๋ฉ์ this๊ฐ ํน์ ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ๋๋ก ๋ช ์์ ์ผ๋ก ์ฐ๊ฒฐ์ํค๋ ๊ธฐ์ ์ ๋๋ค.
JavaScript ๋ฐ์ธ๋ฉ ๋ฉ์๋
1. bind() ๋ฉ์๋
var obj = {
name: 'Original',
sayName: function() {
console.log('My name is ' + this.name);
}
};
// ์ผ๋ฐ ํธ์ถ - this ์์ค
var detachedMethod = obj.sayName;
detachedMethod(); // My name is undefined
// bind()๋ก ๋ฐ์ธ๋ฉ
var boundMethod = obj.sayName.bind(obj);
boundMethod(); // My name is Original
// ๋ค๋ฅธ ๊ฐ์ฒด์ ๋ฐ์ธ๋ฉ
var anotherObj = { name: 'Another' };
var anotherBound = obj.sayName.bind(anotherObj);
anotherBound(); // My name is Another
2. call() ๋ฉ์๋
function greet(greeting, punctuation) {
console.log(greeting + ', ' + this.name + punctuation);
}
var person = { name: 'Alice' };
// call๋ก ์ฆ์ ์คํํ๋ฉฐ this ๋ฐ์ธ๋ฉ
greet.call(person, 'Hello', '!'); // Hello, Alice!
greet.call(person, 'Hi', '.'); // Hi, Alice.
3. apply() ๋ฉ์๋
function introduce(hobby1, hobby2) {
console.log(`์ ๋ ${this.name}์ด๊ณ , ${hobby1}์ ${hobby2}๋ฅผ ์ข์ํฉ๋๋ค.`);
}
var person = { name: '๋ฐ๋ฏผ์' };
// apply๋ก ๋ฐฐ์ด ํํ์ ์ธ์ ์ ๋ฌ
introduce.apply(person, ['๋
์', '์ํ๊ฐ์']);
// ์ ๋ ๋ฐ๋ฏผ์์ด๊ณ , ๋
์์ ์ํ๊ฐ์์ ์ข์ํฉ๋๋ค.
๐ ์ฐจ์ด์ :
- bind(): ์๋ก์ด ํจ์๋ฅผ ๋ฐํ (๋์ค์ ํธ์ถ)
- call(): ์ฆ์ ํจ์ ์คํ, ์ธ์๋ฅผ ๊ฐ๋ณ์ ์ผ๋ก ์ ๋ฌ
- apply(): ์ฆ์ ํจ์ ์คํ, ์ธ์๋ฅผ ๋ฐฐ์ด๋ก ์ ๋ฌ
โ๏ธ React์์์ ๋ฐ์ธ๋ฉ
ํจ์ํ ์ปดํฌ๋ํธ์์์ ์ด๋ฒคํธ ๋ฐ์ธ๋ฉ
import React, { useState } from 'react';
function ColorChanger() {
const [color, setColor] = useState('white');
const [message, setMessage] = useState('์ด๊ธฐ ๋ฉ์์ง');
// ์ด๋ฒคํธ ํธ๋ค๋ฌ ํจ์
const handleColorChange = () => {
setColor(color === 'white' ? 'blue' : 'white');
};
const handleMessageChange = (newMessage) => {
setMessage(newMessage);
};
return (
<div>
<button
onClick={handleColorChange}
style={{ backgroundColor: color, padding: '10px' }}
>
๋ฐฐ๊ฒฝ์ ๋ณ๊ฒฝ
</button>
<button onClick={() => handleMessageChange('ํด๋ฆญ๋จ!')}>
๋ฉ์์ง ๋ณ๊ฒฝ
</button>
<p>{message}</p>
</div>
);
}
ํด๋์คํ ์ปดํฌ๋ํธ์์์ ๋ฐ์ธ๋ฉ ์ด์
import React, { Component } from 'react';
class EventBindingExample extends Component {
constructor(props) {
super(props);
this.state = {
message: 'Hello',
count: 0
};
// ๋ฐฉ๋ฒ 1: constructor์์ ๋ฐ์ธ๋ฉ
this.handleClickBound = this.handleClickBound.bind(this);
}
// ๋ฐ์ธ๋ฉ ์๋ ๋ฉ์๋ (์๋ฌ ๋ฐ์)
handleClickError() {
// TypeError: Cannot read property 'setState' of undefined
this.setState({
message: 'Error occurred!'
});
}
// ๋ฐฉ๋ฒ 1: bind()๋ฅผ ์ฌ์ฉํ ๋ฉ์๋
handleClickBound() {
this.setState({
message: 'Bound method works!',
count: this.state.count + 1
});
}
// ๋ฐฉ๋ฒ 2: ํ์ดํ ํจ์ (์๋ ๋ฐ์ธ๋ฉ)
handleClickArrow = () => {
this.setState({
message: 'Arrow function works!',
count: this.state.count + 1
});
}
render() {
return (
<div>
<p>{this.state.message}</p>
<p>Count: {this.state.count}</p>
{/* ์๋ฌ ๋ฐ์ */}
<button onClick={this.handleClickError}>
์๋ฌ ๋ฒํผ
</button>
{/* ์ฌ๋ฐ๋ฅธ ๋ฐ์ธ๋ฉ */}
<button onClick={this.handleClickBound}>
Bind ๋ฒํผ
</button>
{/* ํ์ดํ ํจ์ */}
<button onClick={this.handleClickArrow}>
ํ์ดํ ํจ์ ๋ฒํผ
</button>
{/* ์ธ๋ผ์ธ ๋ฐ์ธ๋ฉ (์ฑ๋ฅ ์ด์ ์์) */}
<button onClick={this.handleClickError.bind(this)}>
์ธ๋ผ์ธ ๋ฐ์ธ๋
</button>
</div>
);
}
}
๐ ๏ธ ๋ฐ์ธ๋ฉ ๋ฌธ์ ํด๊ฒฐ ๋ฐฉ๋ฒ
1. Constructor์์ ๋ฐ์ธ๋ฉ (๊ถ์ฅ)
class MyComponent extends Component {
constructor(props) {
super(props);
// constructor์์ ํ ๋ฒ๋ง ๋ฐ์ธ๋ฉ
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// this๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ๋ฐ์ธ๋ฉ๋จ
}
}
2. ํ์ดํ ํจ์ ์ฌ์ฉ (์ต์ ๊ถ์ฅ)
class MyComponent extends Component {
// ํด๋์ค ํ๋ ๋ฌธ๋ฒ๊ณผ ํ์ดํ ํจ์
handleClick = () => {
// ์๋์ผ๋ก this๊ฐ ๋ฐ์ธ๋ฉ๋จ
this.setState({ clicked: true });
}
render() {
return <button onClick={this.handleClick}>ํด๋ฆญ</button>;
}
}
3. ํจ์ํ ์ปดํฌ๋ํธ + Hooks (ํ์ฌ ํ์ค)
function MyComponent() {
const [clicked, setClicked] = useState(false);
const handleClick = useCallback(() => {
setClicked(true);
}, []);
return <button onClick={handleClick}>ํด๋ฆญ</button>;
}
๐ ์ค์ ์์ : Toggle ์ปดํฌ๋ํธ
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {
isToggleOn: true,
clickCount: 0
};
// ๋ฐ์ธ๋ฉ ํ์
this.handleToggle = this.handleToggle.bind(this);
}
handleToggle() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn,
clickCount: prevState.clickCount + 1
}));
}
render() {
return (
<div>
<button onClick={this.handleToggle}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
<p>ํด๋ฆญ ํ์: {this.state.clickCount}</p>
</div>
);
}
}
// ํจ์ํ ์ปดํฌ๋ํธ ๋ฒ์ (๊ถ์ฅ)
function ToggleFunctional() {
const [isToggleOn, setIsToggleOn] = useState(true);
const [clickCount, setClickCount] = useState(0);
const handleToggle = useCallback(() => {
setIsToggleOn(prev => !prev);
setClickCount(prev => prev + 1);
}, []);
return (
<div>
<button onClick={handleToggle}>
{isToggleOn ? 'ON' : 'OFF'}
</button>
<p>ํด๋ฆญ ํ์: {clickCount}</p>
</div>
);
}
๐ฏ ํต์ฌ ์ ๋ฆฌ
this ๋ฐ์ธ๋ฉ ๊ท์น
- ์ ์ญ ํธ์ถ: this → ์ ์ญ ๊ฐ์ฒด (window/global)
- ๋ฉ์๋ ํธ์ถ: this → ํธ์ถํ ๊ฐ์ฒด
- ์์ฑ์ ํธ์ถ: this → ์๋ก ์์ฑ๋๋ ์ธ์คํด์ค
- ๋ช ์์ ๋ฐ์ธ๋ฉ: bind/call/apply๋ก ์ง์ ํ ๊ฐ์ฒด
React์์ ๋ฐ์ธ๋ฉ
- ํจ์ํ ์ปดํฌ๋ํธ: this ๋ฐ์ธ๋ฉ ๋ถํ์ (Hooks ์ฌ์ฉ)
- ํด๋์คํ ์ปดํฌ๋ํธ: constructor ๋ฐ์ธ๋ฉ ๋๋ ํ์ดํ ํจ์ ๊ถ์ฅ
๋ฐ์ธ๋ฉ ๋ฐฉ๋ฒ ๋น๊ต
| ๋ฐฉ๋ฒ | ์ฅ์ | ๋จ์ |
| constructor ๋ฐ์ธ๋ฉ | ์ฑ๋ฅ ์ข์, ํ ๋ฒ๋ง ์คํ | ์ฝ๋๊ฐ ๊ธธ์ด์ง |
| ํ์ดํ ํจ์ | ๊ฐ๊ฒฐํจ, ์๋ ๋ฐ์ธ๋ฉ | ํด๋์ค ํ๋ ๋ฌธ๋ฒ ํ์ |
| ์ธ๋ผ์ธ ๋ฐ์ธ๋ฉ | ๊ฐ๋จํจ | ๋งค๋ฒ ์ ํจ์ ์์ฑ (์ฑ๋ฅ ์ ํ) |
๐ก ์ถ๊ฐ ํ
- ES6+ ํ๊ฒฝ์์๋ ํ์ดํ ํจ์๋ฅผ ์ ๊ทน ํ์ฉํ์ธ์
- ์ฑ๋ฅ์ ๊ณ ๋ คํ ๋๋ constructor ๋ฐ์ธ๋ฉ์ ์ ํํ์ธ์
- ํจ์ํ ์ปดํฌ๋ํธ + Hooks๊ฐ ํ์ฌ React ๊ฐ๋ฐ์ ํ์ค์ ๋๋ค
- strict mode์์๋ ์ ์ญ this๊ฐ undefined๊ฐ ๋ฉ๋๋ค
- ๋๋ฒ๊น ์์๋ console.log(this)๋ก ๋ฐ์ธ๋ฉ ์ํ๋ฅผ ํ์ธํ์ธ์