Pink Bobblehead Bunny ๐Ÿš€ JavaScript this์™€ ๋ฐ”์ธ๋”ฉ ์™„๋ฒฝ ๊ฐ€์ด๋“œ
 

๐Ÿš€ JavaScript this์™€ ๋ฐ”์ธ๋”ฉ ์™„๋ฒฝ ๊ฐ€์ด๋“œ

๐Ÿ“Œ 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();

๐Ÿ“ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:

  1. ํ™”์‚ดํ‘œ ํ•จ์ˆ˜ ์‚ฌ์šฉ
  2. bind() ๋ฉ”์„œ๋“œ ์‚ฌ์šฉ
  3. ๋ณ€์ˆ˜์— 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 ๋ฐ”์ธ๋”ฉ ๊ทœ์น™

  1. ์ „์—ญ ํ˜ธ์ถœ: this → ์ „์—ญ ๊ฐ์ฒด (window/global)
  2. ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ: this → ํ˜ธ์ถœํ•œ ๊ฐ์ฒด
  3. ์ƒ์„ฑ์ž ํ˜ธ์ถœ: this → ์ƒˆ๋กœ ์ƒ์„ฑ๋˜๋Š” ์ธ์Šคํ„ด์Šค
  4. ๋ช…์‹œ์  ๋ฐ”์ธ๋”ฉ: bind/call/apply๋กœ ์ง€์ •ํ•œ ๊ฐ์ฒด

React์—์„œ ๋ฐ”์ธ๋”ฉ

  • ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ: this ๋ฐ”์ธ๋”ฉ ๋ถˆํ•„์š” (Hooks ์‚ฌ์šฉ)
  • ํด๋ž˜์Šคํ˜• ์ปดํฌ๋„ŒํŠธ: constructor ๋ฐ”์ธ๋”ฉ ๋˜๋Š” ํ™”์‚ดํ‘œ ํ•จ์ˆ˜ ๊ถŒ์žฅ

๋ฐ”์ธ๋”ฉ ๋ฐฉ๋ฒ• ๋น„๊ต

๋ฐฉ๋ฒ• ์žฅ์  ๋‹จ์ 
constructor ๋ฐ”์ธ๋”ฉ ์„ฑ๋Šฅ ์ข‹์Œ, ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰ ์ฝ”๋“œ๊ฐ€ ๊ธธ์–ด์ง
ํ™”์‚ดํ‘œ ํ•จ์ˆ˜ ๊ฐ„๊ฒฐํ•จ, ์ž๋™ ๋ฐ”์ธ๋”ฉ ํด๋ž˜์Šค ํ•„๋“œ ๋ฌธ๋ฒ• ํ•„์š”
์ธ๋ผ์ธ ๋ฐ”์ธ๋”ฉ ๊ฐ„๋‹จํ•จ ๋งค๋ฒˆ ์ƒˆ ํ•จ์ˆ˜ ์ƒ์„ฑ (์„ฑ๋Šฅ ์ €ํ•˜)

๐Ÿ’ก ์ถ”๊ฐ€ ํŒ

  1. ES6+ ํ™˜๊ฒฝ์—์„œ๋Š” ํ™”์‚ดํ‘œ ํ•จ์ˆ˜๋ฅผ ์ ๊ทน ํ™œ์šฉํ•˜์„ธ์š”
  2. ์„ฑ๋Šฅ์„ ๊ณ ๋ คํ•  ๋•Œ๋Š” constructor ๋ฐ”์ธ๋”ฉ์„ ์„ ํƒํ•˜์„ธ์š”
  3. ํ•จ์ˆ˜ํ˜• ์ปดํฌ๋„ŒํŠธ + Hooks๊ฐ€ ํ˜„์žฌ React ๊ฐœ๋ฐœ์˜ ํ‘œ์ค€์ž…๋‹ˆ๋‹ค
  4. strict mode์—์„œ๋Š” ์ „์—ญ this๊ฐ€ undefined๊ฐ€ ๋ฉ๋‹ˆ๋‹ค
  5. ๋””๋ฒ„๊น… ์‹œ์—๋Š” console.log(this)๋กœ ๋ฐ”์ธ๋”ฉ ์ƒํƒœ๋ฅผ ํ™•์ธํ•˜์„ธ์š”